diff --git a/.babelrc b/.babelrc index 6858a5f69..5c4f6c697 100644 --- a/.babelrc +++ b/.babelrc @@ -1,11 +1,9 @@ { - "presets": [ - "@babel/env", - "@babel/typescript", - "@babel/react" - ], + "presets": ["@babel/env", "@babel/typescript", ["@babel/react", {"runtime": "automatic"}]], "plugins": [ - "@babel/plugin-proposal-class-properties", - "@babel/proposal-object-rest-spread" + ["@babel/plugin-proposal-decorators", {"legacy": true}], + ["@babel/plugin-proposal-class-properties", {"loose": false}], + "@babel/proposal-object-rest-spread", + "@babel/plugin-proposal-optional-chaining" ] } diff --git a/.dockerignore b/.dockerignore index db54b2f6a..dd56869e0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,65 @@ -# Ignore files not needed for building and running to keep image size low. -.git/ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Secrets +*.secret + +# Dependency directories node_modules/ +# Git directory +.git/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# macOS custom attributes +.DS_Store + +# VisualStudio +.vscode + +# Intellij +.idea + +# Personal flood config +config.js + +# Distribution files +dist/ +dist-pkg/ + +# Runtime files +run/ + +# Vim temp files +*.swp + +# JSDoc output +docs/ +out/ diff --git a/.eslintignore b/.eslintignore index ee52212ef..3bbe3454b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,7 @@ +.eslintrc.js /node_modules -/server/assets +/dist **/*.d.ts /docs +coverage/ +cypress/ diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 3d49eef6b..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,41 +0,0 @@ -module.exports = { - extends: ['airbnb', 'plugin:import/errors', 'plugin:import/warnings', 'prettier'], - parser: 'babel-eslint', - plugins: ['import'], - rules: { - 'arrow-parens': 0, - 'class-methods-use-this': 0, - 'consistent-return': 0, - 'implicit-arrow-linebreak': 0, - 'import/no-extraneous-dependencies': 0, - 'import/prefer-default-export': 0, - 'max-len': [ - 'error', - { - code: 120, - ignoreRegExpLiterals: true, - ignoreStrings: true, - ignoreTemplateLiterals: true, - ignoreUrls: true, - }, - ], - 'no-console': 0, - 'no-param-reassign': 0, - 'no-plusplus': 0, - 'no-underscore-dangle': [2, {allow: ['_id']}], - 'no-unused-vars': [0, { "argsIgnorePattern": "^_" }], - - 'object-curly-newline': 0, - 'object-curly-spacing': 0, - 'prefer-destructuring': [ - 2, - { - array: false, - object: true, - }, - { - enforceForRenamedProperties: false, - }, - ], - }, -}; diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..ebdfd4a29 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,29 @@ +{ + "extends": ["plugin:@typescript-eslint/recommended", "prettier", "prettier/@typescript-eslint"], + + "parserOptions": { + "project": "./tsconfig.json" + }, + + "env": { + "browser": false, + "node": true + }, + + "rules": { + "import/no-extraneous-dependencies": 0, + "no-underscore-dangle": ["error", {"allow": ["_id"]}], + "@typescript-eslint/lines-between-class-members": ["error", "always", {"exceptAfterSingleLine": true}], + "@typescript-eslint/no-unused-vars": ["error", {"argsIgnorePattern": "^_"}] + }, + "overrides": [ + { + "files": ["*.js", "*.jsx"], + "extends": ["prettier", "prettier/react"], + "parser": "babel-eslint", + "rules": { + "@typescript-eslint/no-var-requires": 0 + } + } + ] +} diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index fc1e92f8e..fd6c02a14 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,10 +1,6 @@ # Contributing We love contributions from everyone. -By participating in this project, -you agree to abide by the thoughtbot [code of conduct]. - - [code of conduct]: https://thoughtbot.com/open-source-code-of-conduct # Issue @@ -13,24 +9,3 @@ See [ISSUE_TEMPLATE](ISSUE_TEMPLATE). # Pull request See [PULL_REQUEST_TEMPLATE.md](PULL_REQUEST_TEMPLATE.md). - -# Code quality - -+ Be sure to use 2 spaces instead of tabulations. - -# Labels - -Category | Label(s) | Color(s) ---- | --- | --- -Platform | ![](labels/bsd.png) ![](labels/docker.png) ![](labels/linux.png) ![](labels/macOS.png) ![](labels/windows.png) | #BFD4F2 -Problems | ![](labels/bug.png) ![](labels/security.png) | #EE3F46 -Severity | ![](labels/critical.png) | #B60205 -Type | ![](labels/code.png) ![](labels/design.png) ![](labels/documentation.png) ![](labels/test.png) | #FFC274 -Feedback | ![](labels/discussion.png) ![](labels/question.png) | #CC317C -Improvements | ![](labels/enhancement.png) ![](labels/optimization.png) ![](labels/performance.png) | #5EBEFF -Help | ![](labels/help%20wanted.png) | #76C3A9 -Additions | ![](labels/feature.png) | #90C954 -Pending | ![](labels/can't%20reproduce.png) ![](labels/in%20progress.png) ![](labels/more%20info%20needed.png) ![](labels/waiting%20feedback.png) ![](labels/watchlist.png) | #FBCA04 -Inactive | ![](labels/duplicate.png) ![](labels/invalid.png) ![](labels/not%20a%20bug.png) ![](labels/on%20hold.png) ![](labels/wontfix.png) | #D2DAE1 - -**Note (if there is a need to add labels)**: in order to take a sharp screenshot of labels with Firefox: Right click the label => Inspect element => Right click the element on the inspector => Screenshot Node diff --git a/.github/ISSUE_TEMPLATE/01_bug_report.md b/.github/ISSUE_TEMPLATE/01_bug_report.md index 3663f13e4..fde199cd9 100644 --- a/.github/ISSUE_TEMPLATE/01_bug_report.md +++ b/.github/ISSUE_TEMPLATE/01_bug_report.md @@ -1,44 +1,53 @@ --- -name: "🐞 Bug Report" -about: "Report a general bug in flood" +name: '🐞 Bug Report' +about: 'Report a general bug in flood' --- + Type: Bug Report - [ ] Try to follow the update procedure described in the README and try again before opening this issue. -- [ ] Please check the [F.A.Q.](https://github.com/Flood-UI/flood/wiki/F.A.Q.). -- [ ] Please check the [Troubleshooting](https://github.com/Flood-UI/flood/wiki/Troubleshooting) wiki section. ## Your Environment + -* Version used: - + Version (stable release) `git --no-pager tag` - + Commit ID (development release) `git --no-pager log -1` -* Environment name and version: - + Node.js version `node --version` - + npm version `npm --version` - + Web browser `name and version` -* Operating System and version: + +- Version used: + - Version (stable release) + - Commit ID (development release) `git --no-pager log -1` +- Environment name and version: + - Node.js version `node --version` + - npm version `npm --version` + - Web browser `name and version` +- Operating system and version: +- Torrent client and version: ## Summary + ## Expected Behavior + ## Current Behavior + ## Possible Solution + ## Steps to Reproduce + + 1. 2. 3. 4. ## Context + diff --git a/.github/ISSUE_TEMPLATE/02_documentation_issue.md b/.github/ISSUE_TEMPLATE/02_documentation_issue.md new file mode 100644 index 000000000..85a8d4262 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02_documentation_issue.md @@ -0,0 +1,10 @@ +--- +name: '📚 Documentation Issue' +about: 'Report an issue or missing part in the documentation' +--- + +Type: Documentation Issue + +## Summary + + diff --git a/.github/ISSUE_TEMPLATE/02_feature_request.md b/.github/ISSUE_TEMPLATE/02_feature_request.md deleted file mode 100644 index 5eebf082e..000000000 --- a/.github/ISSUE_TEMPLATE/02_feature_request.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: "💡 Feature Request" -about: "Suggest an idea for this project" ---- -Type: Feature Request - -- [ ] If you want to contribute to the project please review the [contributing guidelines](https://github.com/Flood-UI/flood/blob/master/.github/CONTRIBUTING.md). - -## Summary - - - -## Idea of implementation - diff --git a/.github/ISSUE_TEMPLATE/03_security_vulnerability.md b/.github/ISSUE_TEMPLATE/03_security_vulnerability.md deleted file mode 100644 index 0d15d941a..000000000 --- a/.github/ISSUE_TEMPLATE/03_security_vulnerability.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: "🔒 Security Vulnerability" -about: "Report a security vulnerability in flood" ---- -PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, SEE BELOW. - -If you discover a security vulnerability within flood, please send an e-mail to jfurrow (me@johnfurrow.com) or noraj (cybersecurity@tutamail.com). diff --git a/.github/ISSUE_TEMPLATE/04_documentation_issue.md b/.github/ISSUE_TEMPLATE/04_documentation_issue.md deleted file mode 100644 index 54973a9a3..000000000 --- a/.github/ISSUE_TEMPLATE/04_documentation_issue.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: "📚 Documentation Issue" -about: 'Report an issue or missing part in the documentation' ---- -Type: Documentation Issue - -## Summary - diff --git a/.github/ISSUE_TEMPLATE/05_question.md b/.github/ISSUE_TEMPLATE/05_question.md deleted file mode 100644 index 6f5fe9cc2..000000000 --- a/.github/ISSUE_TEMPLATE/05_question.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: "❓ Question" -about: "Ask your questions here" ---- -Type: Question - -## Question diff --git a/.github/ISSUE_TEMPLATE/06_discussion.md b/.github/ISSUE_TEMPLATE/06_discussion.md deleted file mode 100644 index 5de7e1b53..000000000 --- a/.github/ISSUE_TEMPLATE/06_discussion.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: "🎙️ Discussion" -about: "Start a discussion here" ---- -Type: Discussion - -## Discussion diff --git a/.github/ISSUE_TEMPLATE/07_support.md b/.github/ISSUE_TEMPLATE/07_support.md deleted file mode 100644 index 3d449bcb1..000000000 --- a/.github/ISSUE_TEMPLATE/07_support.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -name: "🆘 Support" -about: "Ask for help on Discord" ---- -If you need support, ask on Discord https://discord.gg/Z7yR5Uf diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..78c77772a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: true +contact_links: + - name: 💡 Feature Request + url: https://github.com/jesec/flood/discussions/new + about: Share ideas for new features + - name: ❓ Question + url: https://github.com/jesec/flood/discussions/new + about: General questions + - name: 🎙️ Discussion + url: https://github.com/jesec/flood/discussions/new + about: Start a discussion here + - name: 🆘 Support + url: https://discord.gg/Z7yR5Uf + about: Join Discord for real-time support diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fab500439..3f9055e0e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,36 +1,21 @@ ## Description + ## Related Issue - - - - -## Motivation and Context - + -## How Has This Been Tested? - - - +## Screenshots -## Screenshots (if appropriate): + ## Types of changes + + - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - -## Checklist: - - -- [ ] My code follows the code style of this project. -- [ ] My change requires a change to the documentation. -- [ ] I have updated the documentation accordingly. -- [ ] I have read the **CONTRIBUTING** document. -- [ ] I have added tests to cover my changes. -- [ ] All new and existing tests passed. diff --git a/.github/labels/bsd.png b/.github/labels/bsd.png deleted file mode 100644 index 67e4c3170..000000000 Binary files a/.github/labels/bsd.png and /dev/null differ diff --git a/.github/labels/bug.png b/.github/labels/bug.png deleted file mode 100644 index 649966fec..000000000 Binary files a/.github/labels/bug.png and /dev/null differ diff --git a/.github/labels/can't reproduce.png b/.github/labels/can't reproduce.png deleted file mode 100644 index bc78a2e21..000000000 Binary files a/.github/labels/can't reproduce.png and /dev/null differ diff --git a/.github/labels/code.png b/.github/labels/code.png deleted file mode 100644 index 954436ea4..000000000 Binary files a/.github/labels/code.png and /dev/null differ diff --git a/.github/labels/critical.png b/.github/labels/critical.png deleted file mode 100644 index f66d5a5ba..000000000 Binary files a/.github/labels/critical.png and /dev/null differ diff --git a/.github/labels/design.png b/.github/labels/design.png deleted file mode 100644 index 8435f1b4f..000000000 Binary files a/.github/labels/design.png and /dev/null differ diff --git a/.github/labels/discussion.png b/.github/labels/discussion.png deleted file mode 100644 index 920135999..000000000 Binary files a/.github/labels/discussion.png and /dev/null differ diff --git a/.github/labels/docker.png b/.github/labels/docker.png deleted file mode 100644 index 31b006f9a..000000000 Binary files a/.github/labels/docker.png and /dev/null differ diff --git a/.github/labels/documentation.png b/.github/labels/documentation.png deleted file mode 100644 index 146b7fc69..000000000 Binary files a/.github/labels/documentation.png and /dev/null differ diff --git a/.github/labels/duplicate.png b/.github/labels/duplicate.png deleted file mode 100644 index accea497e..000000000 Binary files a/.github/labels/duplicate.png and /dev/null differ diff --git a/.github/labels/enhancement.png b/.github/labels/enhancement.png deleted file mode 100644 index 78f452f50..000000000 Binary files a/.github/labels/enhancement.png and /dev/null differ diff --git a/.github/labels/feature.png b/.github/labels/feature.png deleted file mode 100644 index 86eff7b9d..000000000 Binary files a/.github/labels/feature.png and /dev/null differ diff --git a/.github/labels/help wanted.png b/.github/labels/help wanted.png deleted file mode 100644 index efc2c3f4e..000000000 Binary files a/.github/labels/help wanted.png and /dev/null differ diff --git a/.github/labels/in progress.png b/.github/labels/in progress.png deleted file mode 100644 index 671ff88c8..000000000 Binary files a/.github/labels/in progress.png and /dev/null differ diff --git a/.github/labels/invalid.png b/.github/labels/invalid.png deleted file mode 100644 index 3f196b63c..000000000 Binary files a/.github/labels/invalid.png and /dev/null differ diff --git a/.github/labels/labels.md b/.github/labels/labels.md deleted file mode 100644 index 676255458..000000000 --- a/.github/labels/labels.md +++ /dev/null @@ -1,16 +0,0 @@ -# Labels - -Category | Label(s) | Color(s) ---- | --- | --- -Platform | ![](bsd.png) ![](docker.png) ![](linux.png) ![](macOS.png) ![](windows.png) | #BFD4F2 -Problems | ![](bug.png) ![](security.png) | #EE3F46 -Severity | ![](critical.png) | #B60205 -Type | ![](code.png) ![](design.png) ![](documentation.png) ![](test.png) | #FFC274 -Feedback | ![](discussion.png) ![](question.png) | #CC317C -Improvements | ![](enhancement.png) ![](optimization.png) ![](performance.png) | #5EBEFF -Help | ![](help%20wanted.png) | #76C3A9 -Additions | ![](feature.png) | #90C954 -Pending | ![](can't%20reproduce.png) ![](in%20progress.png) ![](more%20info%20needed.png) ![](waiting%20feedback.png) ![](watchlist.png) | #FBCA04 -Inactive | ![](duplicate.png) ![](invalid.png) ![](not%20a%20bug.png) ![](on%20hold.png) ![](wontfix.png) | #D2DAE1 - -Note: in order to take a sharp screenshot of labels with Firefox: Right click the label => Inspect element => Right click the element on the inspector => Screenshot Node diff --git a/.github/labels/linux.png b/.github/labels/linux.png deleted file mode 100644 index 06134c62f..000000000 Binary files a/.github/labels/linux.png and /dev/null differ diff --git a/.github/labels/macOS.png b/.github/labels/macOS.png deleted file mode 100644 index b66b3a0e5..000000000 Binary files a/.github/labels/macOS.png and /dev/null differ diff --git a/.github/labels/more info needed.png b/.github/labels/more info needed.png deleted file mode 100644 index 8c1cd3469..000000000 Binary files a/.github/labels/more info needed.png and /dev/null differ diff --git a/.github/labels/not a bug.png b/.github/labels/not a bug.png deleted file mode 100644 index cfaa22701..000000000 Binary files a/.github/labels/not a bug.png and /dev/null differ diff --git a/.github/labels/on hold.png b/.github/labels/on hold.png deleted file mode 100644 index 597178e91..000000000 Binary files a/.github/labels/on hold.png and /dev/null differ diff --git a/.github/labels/optimization.png b/.github/labels/optimization.png deleted file mode 100644 index ce6ba2402..000000000 Binary files a/.github/labels/optimization.png and /dev/null differ diff --git a/.github/labels/performance.png b/.github/labels/performance.png deleted file mode 100644 index 83d045e31..000000000 Binary files a/.github/labels/performance.png and /dev/null differ diff --git a/.github/labels/question.png b/.github/labels/question.png deleted file mode 100644 index d2d008304..000000000 Binary files a/.github/labels/question.png and /dev/null differ diff --git a/.github/labels/security.png b/.github/labels/security.png deleted file mode 100644 index fb89937c4..000000000 Binary files a/.github/labels/security.png and /dev/null differ diff --git a/.github/labels/test.png b/.github/labels/test.png deleted file mode 100644 index eee8d01fa..000000000 Binary files a/.github/labels/test.png and /dev/null differ diff --git a/.github/labels/waiting feedback.png b/.github/labels/waiting feedback.png deleted file mode 100644 index b483b4f0c..000000000 Binary files a/.github/labels/waiting feedback.png and /dev/null differ diff --git a/.github/labels/watchlist.png b/.github/labels/watchlist.png deleted file mode 100644 index 107504f47..000000000 Binary files a/.github/labels/watchlist.png and /dev/null differ diff --git a/.github/labels/windows.png b/.github/labels/windows.png deleted file mode 100644 index 756cb66eb..000000000 Binary files a/.github/labels/windows.png and /dev/null differ diff --git a/.github/labels/wontfix.png b/.github/labels/wontfix.png deleted file mode 100644 index 6ead67f47..000000000 Binary files a/.github/labels/wontfix.png and /dev/null differ diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..e42e9a53d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,30 @@ +name: Build + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + runs-on: ubuntu-20.04 + + strategy: + matrix: + node: [14, 15] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + + - run: npm ci --no-optional + - run: npm run build + - run: npm run start -- --help diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 000000000..fad31f949 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,30 @@ +name: Check + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + check: + runs-on: ubuntu-20.04 + + strategy: + fail-fast: false + matrix: + node: [15] + check: [check-compiled-i18n, check-source-formatting, check-types, lint] + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + + - run: npm ci --no-optional + - run: npm run ${{ matrix.check }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..20503234b --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,51 @@ +name: 'CodeQL' + +on: + push: + branches: [master] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + # Runs at 06:00 UTC on Sun + - cron: '0 6 * * 0' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-20.04 + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['javascript'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/publish-rolling.yml b/.github/workflows/publish-rolling.yml new file mode 100644 index 000000000..8f5a2f3d4 --- /dev/null +++ b/.github/workflows/publish-rolling.yml @@ -0,0 +1,136 @@ +name: Publish rolling build + +on: + push: + branches: + - master + +jobs: + npm: + runs-on: ubuntu-20.04 + + strategy: + matrix: + node: [15] + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + registry-url: 'https://registry.npmjs.org' + + - run: npm ci --no-optional + + - name: Tag rolling release + run: | + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + npm version --no-git-tag-version 0.0.0-master.`git rev-parse --short HEAD` + + - name: Use @${{ github.actor }} scope + run: | + jq '.name = "@${{ github.actor }}/flood"' package.json > package.new.json + mv package.new.json package.json + + - run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + docker: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + with: + buildkitd-flags: --debug + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Tag rolling release + run: | + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + npm version --no-git-tag-version 0.0.0-master.`git rev-parse --short HEAD` + + - name: Publish flood to Docker Hub + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: true + tags: jesec/flood:master + + - run: sleep 10 + + - name: Publish rtorrent-flood to Docker Hub + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile.rtorrent + platforms: linux/amd64,linux/arm64 + push: true + tags: | + jesec/rtorrent-flood:master + jesec/rtorrent-flood:latest + build-args: | + FLOOD_VERSION=master + RTORRENT_VERSION=master + + pkg: + runs-on: ubuntu-20.04 + + strategy: + matrix: + node: [15] + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + registry-url: 'https://registry.npmjs.org' + + - run: npm ci --no-optional + - run: sudo npm install -g pkg + + - name: Tag rolling release + run: | + npm version --no-git-tag-version 0.0.0-master.`git rev-parse --short HEAD` + + - name: Build executables + run: | + npm run build-pkg + + - name: 'Upload executable: Linux' + uses: actions/upload-artifact@v2 + with: + name: flood-linux + path: dist-pkg/flood-linux + + - name: 'Upload executable: MacOS' + uses: actions/upload-artifact@v2 + with: + name: flood-macos + path: dist-pkg/flood-macos + + - name: 'Upload executable: Windows' + uses: actions/upload-artifact@v2 + with: + name: flood-win + path: dist-pkg/flood-win.exe diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..49a44816d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,91 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-20.04 + + strategy: + matrix: + node: [15] + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + registry-url: 'https://registry.npmjs.org' + + - run: sudo npm i -g pkg + - run: npm ci + + - run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - run: npm run build-pkg + - run: git fetch origin ${{ github.ref }} + - run: git for-each-ref ${{ github.ref }} --format="%(contents)" > body + + - name: Get the version + id: get_version + run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} + + - name: Create Release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: Release ${{ steps.get_version.outputs.VERSION }} + body_path: body + files: | + dist-pkg/flood-linux + dist-pkg/flood-macos + dist-pkg/flood-win.exe + + release-docker: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + + - name: Parse semantic version + id: parse_semver + run: | + full_version=`echo ${GITHUB_REF/refs\/tags\/v/}` + major_version=`echo $full_version | cut -d'.' -f1` + minor_version=`echo $full_version | cut -d'.' -f2` + major_minor=`echo $major_version.$minor_version` + echo ::set-output name=MAJOR_VERSION::$major_version + echo ::set-output name=MAJOR_MINOR::$major_minor + echo ::set-output name=FULL_VERSION::$full_version + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Publish to Docker Hub + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: true + tags: | + jesec/flood:latest + jesec/flood:${{ steps.parse_semver.outputs.MAJOR_VERSION }} + jesec/flood:${{ steps.parse_semver.outputs.MAJOR_MINOR }} + jesec/flood:${{ steps.parse_semver.outputs.FULL_VERSION }} diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml new file mode 100644 index 000000000..0f26f36b5 --- /dev/null +++ b/.github/workflows/test-backend.yml @@ -0,0 +1,36 @@ +name: Test backend + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + test-backend: + runs-on: ubuntu-20.04 + + strategy: + matrix: + node: [14, 15] + fail-fast: false + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js ${{ matrix.node }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + + - run: sudo add-apt-repository -y ppa:qbittorrent-team/qbittorrent-unstable + - run: sudo add-apt-repository -y ppa:transmissionbt/ppa + - run: sudo apt-get install -y rtorrent qbittorrent-nox transmission-daemon + + - run: npm ci --no-optional + - run: npm test || npm test || npm test + + - uses: codecov/codecov-action@v1 + if: matrix.node == 15 diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml new file mode 100644 index 000000000..093924cad --- /dev/null +++ b/.github/workflows/test-frontend.yml @@ -0,0 +1,42 @@ +name: Test frontend + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + test-frontend: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js 15 + uses: actions/setup-node@v1 + with: + node-version: 15 + + - run: sudo apt-get install -y rtorrent + + - run: sudo npm i -g wait-on + - run: sudo CYPRESS_INSTALL_BINARY=0 npm i -g cypress + + - run: npm ci + - run: npm run build-assets + + - run: node scripts/testsetup.js -p 4200 & echo $! > testenv.pid + - run: wait-on tcp:4200 + + - run: npm link cypress + - run: cypress install + - uses: cypress-io/github-action@v2 + with: + install: false + record: true + env: + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 7fd7cb69b..1fb8b5fe9 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ pids *.seed *.pid.lock +# Secrets +*.secret + # Dependency directories node_modules/ @@ -44,10 +47,12 @@ node_modules/ # Personal flood config config.js -# Flood server -server/db -server/assets -server/temp +# Distribution files +dist/ +dist-pkg/ + +# Runtime files +run/ # Vim temp files *.swp @@ -55,3 +60,10 @@ server/temp # JSDoc output docs/ out/ + +# Coverage reports +coverage/ + +# Test results +cypress/videos +cypress/screenshots diff --git a/.jsdoc.json b/.jsdoc.json deleted file mode 100644 index 43e25e30c..000000000 --- a/.jsdoc.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc"] - }, - "source": { - "include": ["client", "server", "shared", "package.json", "README.md"], - "includePattern": ".js$", - "excludePattern": "(node_modules/|docs)" - }, - "plugins": [ - "plugins/markdown" - ], - "templates": { - "cleverLinks": false, - "monospaceLinks": true, - "useLongnameInNav": false, - "showInheritedInNav": true - }, - "opts": { - "destination": "./docs/", - "encoding": "utf8", - "private": true, - "recurse": true, - "template": "./node_modules/minami" - } -} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..e9fb1c38b --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +# Distribution files +dist/ +dist-pkg/ + +# Coverage reports +coverage/ diff --git a/.travis.yml b/.travis.yml index 3f6fb9707..e5a8f34a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,14 @@ -dist: trusty +dist: focal language: node_js node_js: - - '10' - - '12' + - 'node' + - 'lts/*' - '13' matrix: fast_finish: true - allow_failures: - - node_js: 10 -before_script: - - cp config.template.js config.js script: - npm run check-source-formatting - npm run lint - npm run check-types - - npm run test - npm run build + - npm run test diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..04f0d0825 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch development server", + "type": "node", + "request": "launch", + "skipFiles": ["/**"], + "env": { + "NODE_ENV": "development", + "TS_NODE_PROJECT": "${workspaceFolder}/server/tsconfig.json" + }, + "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ts-node-dev", + "runtimeArgs": ["--respawn", "--inspect", "--transpile-only"], + "program": "server/bin/start.ts", + "args": [] + }, + { + "name": "Launch development client", + "type": "chrome", + "request": "launch", + "url": "http://localhost:4200", + "webRoot": "${workspaceFolder}" + } + ] +} diff --git a/ABOUT.md b/ABOUT.md index d76b40ce5..8601ac13d 100644 --- a/ABOUT.md +++ b/ABOUT.md @@ -1,9 +1,15 @@ -# Flood +# Flood -[![Travis CI build status badge](https://img.shields.io/travis/Flood-UI/flood/master.svg?style=flat-square)](https://travis-ci.org/Flood-UI/flood) [![Discord server badge](https://img.shields.io/discord/418267176873623553.svg?style=flat-square)](https://discord.gg/Z7yR5Uf) +[![Latest release badge](https://img.shields.io/npm/v/flood?label=Latest%20Release)](https://www.npmjs.com/flood) -Flood is a monitoring service for [rTorrent](https://github.com/rakshasa/rtorrent). It's a Node.js service that communicates with rTorrent instances and serves a decent web UI for administration. It's a work-in-progress. +[![Github Actions build status badge](https://github.com/jesec/flood/workflows/Build/badge.svg?branch=master&event=push)](https://github.com/jesec/flood/actions) [![Crowdin](https://badges.crowdin.net/flood/localized.svg)](https://crowdin.com/project/flood) [![Discord server badge](https://img.shields.io/discord/418267176873623553.svg?style=flat-square)](https://discord.gg/Z7yR5Uf) + +Flood is a monitoring service for various torrent clients. It's a Node.js service that communicates with your favorite torrent client and serves a decent web UI for administration. This project is based on the [original Flood project](https://github.com/Flood-UI/flood). #### Feedback -If you have a specific issue or bug, please file a GitHub issue. Please join the [Flood Discord server](https://discord.gg/Z7yR5Uf) to discuss feature requests and implementation details. +If you have a specific issue or bug, please file a [GitHub issue](https://github.com/jesec/flood/issues). Please join the [Flood Discord server](https://discord.gg/Z7yR5Uf) to discuss feature requests and implementation details. + +#### More Information + +Check out the [Wiki](https://github.com/jesec/flood/wiki) for more information. diff --git a/CHANGELOG.md b/CHANGELOG.md index e693d5f81..af40ca25b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,43 +1,289 @@ -# Change Log - -## [Unreleased] -* Supports connecting to multiple rtorrent instances (one per user) - * Moved rtorrent configuration to user database - * Prompts user for connection details in UI when can't connect to rtorrent -* Changed `/list/` route to `/overview/` -* Reorganized and renamed component source files -* Removed verbose logging from `HistoryEra` -* Check existing feed items against new download rules -* Switch URL and Label textboxes in Add Feed form to match the Download Rules form -* Rate-limit the SCGI calls to rTorrent - * Sends only one call at a time - * Sends at most one call every 250 miliseconds -* Implement "actity stream" - * The Flood client no longer polls the Flood server on an interval. Instead, - the Flood server polls rTorrent on a more regular interval and emits changes - via an event-stream. This significantly reduces data usage on the Flood client - * Stream covers torrent list, transfer rate summary & history, - torrent taxonomy, and notification count. -* Close event stream after the window/tab has been inactive for 30 seconds -* Refactor development experience, using `Webpack` & `WebpackDevServer` -* Require users to build static assets again +# Changelog + +## [4.3.1] (December 10, 2020) + +- Make theme button always at the bottom of sidebar +- Remove legacy font formats from static assets +- Slightly tweak styles of country flags in peer list + - Better accommodate longer flags + - Display country code on hover +- qBittorrent fixes: + - Attach cookies to URL downloads + - Set trackers +- Transmission fixes: + - Percentage downloaded of contents of a torrent +- Bug fixes: + - API call to get peer list of a non-existent torrent no longer crash Flood server + - Handle file not exist and access denied cases in content download + - Properly handle API call to update password of a user +- Security enhancements: + - Rate limit resource-intensive mediainfo request + - Ensure path is allowed for mediainfo request + - API call to list users no longer receive hashed passwords and client connection settings + - Note: only an authenticated admin user may list users +- New translations + - Czech, thanks to @brezinajn + - Romanian, thanks to @T-z3P +- Bump dependencies + +## [4.3.0] (December 1, 2020) + +- Generate magnet link from torrent +- Add a button to allow user to switch color scheme +- Multi architecture Docker images + - linux/amd64 + - linux/arm64 (new) + - linux/arm/v7 (new) +- Allow to display precise percentage + - Expanded view: 1 decimal place + - Details: 3 decimal places +- Mountpoints with very long paths are ignored by disk usage +- Tags can be attached while adding torrents to qBittorrent (needs qbittorrent/qBittorrent#13882) +- Bug fixes: + - Download destination fallback to rTorrent default destination (Mika-/torrent-control#105) + - Properly catch errors of AddFiles and AddURLs when using qBittorrent + - Display existing trackers in set trackers modal +- New translations + - Spanish, thanks to @vain4us +- Bump dependencies + +Side note: + +I am starting to maintain a distribution of rTorrent, available at [jesec/rtorrent](https://github.com/jesec/rtorrent). It is optimized and small. It uses modern CMake and Bazel build systems. Bazel can also be used for dependency management and to produce statically linked reproducible builds. + +Static binaries (amd64, arm64) can be downloaded via [Github Actions](https://github.com/jesec/rtorrent/actions?query=workflow%3A%22Publish+rolling+build%22). Docker images are also available at [jesec/rtorrent](https://hub.docker.com/r/jesec/rtorrent). + +I made a simple [Dockerfile](https://github.com/jesec/flood/blob/5cc56067c3be6c91ccf94f71d4784be99c2823f8/Dockerfile.rtorrent) to demonstrate how to integrate rTorrent with Flood. + +## [4.2.0] (November 25, 2020) + +- Allow content of a torrent to be streamed directly if supported by browser +- New translations + - Chinese (Traditional), thanks to @vongola12324 + - Dutch, thanks to @vain4us + - French, thanks to @Coosos +- Bump dependencies + +## [4.1.2] (November 21, 2020) + +- qBittorrent fixes: + - Remove existing tags +- New translations + - Dutch, thanks to @vain4us + - German, thanks to @chint95 + +## [4.1.1] (November 18, 2020) + +- Transmission fixes: + - Set tags while adding torrents + - Set trackers +- New translations + - Czech, thanks to Jan Březina + - French, thanks to @Carryozor + - German, thanks to @chint95 + - Romanian, thanks to @T-z3P + +## [4.1.0] (November 17, 2020) + +⚠️ Changes that may require manual attention: ⚠️ + +- Configuration is now schema validated before the start of Flood server + - No action required if you use (preferred and default) CLI configuration interface + - This ensures that when the config.js needs to be updated, the failure happens loud and early + - Check [shared/schema/Config.ts](https://github.com/jesec/flood/blob/master/shared/schema/Config.ts) for more details +- Enforces that the length of secret must be larger than 30 + - Secret can be brute forced locally without interaction with the server + - However, an attacker must get a valid token (generated by proper authentication) first + - If all users are trusted, attackers have no way to get a valid token + - Secret is used to sign authentication tokens but it is NOT linked to the password + - Attacker may log into Flood as any user if they have the secret + - However, they are still constrained by capabilities and settings (such as `--allowedpath`) of Flood + +Other changes: + +- Tag selector preference: + - Single selection + - Multi selection +- UX enhancements to tag selector +- Suggest destination based on selected tag +- `add-urls` and `add-files` API endpoints no longer fail if `destination` property is not provided + - Download destination fallback has been implemented: + - Tag-specific preferred download destination + - Last used download destination + - Default download destination of connected torrent client + - This makes things easier for API users + - No direct impact on Flood itself +- Remember last used "Add Torrents" tab +- Remove center alignment of certain modals to align with global styles +- Disallow browser's input suggestion when tag selector or folder browser is open +- Don't pop up the browser menu on right click while context menu is open +- Experimental standalone (single-executable) builds +- New translations + - German, thanks to @chint95 + - Romanian, thanks to @T-z3P +- Bump dependencies +- Bug fixes: + - Properly handle "error" alerts (display "❗" icon instead of "✅" icon) + +## [4.0.2] (November 11, 2020) + +- New translations + - German, thanks to @chint95 + - Romanian, thanks to @T-z3P + +## [4.0.1] (November 10, 2020) + +- Fix the unreliable clear all notification button +- Bump dependencies + +## [4.0.0] (November 9, 2020) + +- Experimental multi-client support + - qBittorrent + - Transmission +- Stabilized and documented public API endpoints +- Defined and documented internal interfaces, data structures and APIs +- Better documentation for users and developers +- Full migration to TypeScript +- Reasonable test coverages for API endpoints +- Torrent creation support +- Add torrents as completed +- Dropdown selector for existing tags +- Seeding status in status filter +- Set tracker URLs of torrents +- Improved handling of rendering, updating and scrolling of torrent list + - Preliminary tests show that Flood can now handle 500,000 torrents at least in the frontend. + - Note: real-world performance depends on other factors such as method call and deserialization operations in the backend and data transfer between backend and frontend. +- Better performance, less memory and CPU consumption in both frontend and backend +- New translations + - Chinese (Traditional), thanks to @vongola12324 + - Czech, thanks to Jan Březina + - French, thanks to @Zopieux and @Mystere98 + - German, thanks to @chint95 +- Bug fixes +- Security enhancements +- Dockerfile revamp +- Native build tools no longer needed as native dependency is replaced with WebAssembly variant +- Server is packed before distribution, reduced number of dependencies in production, faster installation + +## [3.1.0] (September 4, 2020) + +- Allow to replace main tracker of torrents +- Allow adjustment of visible context menu items +- config.cli: make all configs configurable by options and env +- styles: properly set width of clipboard icon (fixes #26) +- client: hide logout button when auth is disabled +- Hungarian support (#21), thanks to @sfu420 +- New translations: + - Chinese Traditional, thanks to @vongola12324 + - Czech, thanks to @brezina.jn + - Portuguese, thanks to @Zamalor +- Security enhancements: + - Allow restriction on file operations by paths + - Do not bypass authentication token validation with disableUsersAndAuth + - server: prohibit Cross-Origin Resource Sharing + - server: auth: strictly prohibit cross-site cookie +- Minor security fixes: + - rTorrentDeserializer: avoid double unescaping + - SettingsModal: mergeObjects: prevent prototype pollution + - server: setSettings: turn inboundTransformations into a Map to validate dynamic call + - server: be explicit about client app routes + - server: cache index.html into memory +- Minor refactoring and other changes +- Bump dependencies to the latest revisions + +## [3.0.0] (August 25, 2020) + +- BREAKING CHANGES: + - If `baseURI` is set, server will only respond to requests with baseURI. For instance, if you use `location /flood {proxy_pass http://127.0.0.1:3000;}`, you would have to change it to `location /flood {proxy_pass http://127.0.0.1:3000/flood;}`. + - Static assets now use relative paths only. It is no longer needed to recompile after `baseURI` change. + - Location of runtime files are rearranged. New default location for runtime files is `./run` folder. `tempPath` is now made configurable. + - Static assets are relocated to `./dist` folder. You have to change the path from `./server/assets` to `./dist/assets` if you serve static assets from web server. + - Flood will refuse to start if secrets are detected in static assets. Former default secret `flood` and some other weak secrets are no longer accepted. +- A command line interface is added as `config.cli.js`. Rename it to `config.js` and run `npm run start -- --help` for more details. +- With some changes, Flood is now ready for publish to `npm`. You can now use `sudo npm install -g flood` to get a ready-to-use copy of Flood, then run `flood`. It is even easier with `npx`, try `npx flood --help` now. +- Better localization: + - Flood project is now integrated with [Crowdin](https://crwd.in/flood), a renowned translation management system. It is now easier than ever to contribute your translations to Flood. + - Language will now be automatically detected from your browser by default. + - New languages are supported: `Čeština`, `Deutsch`, `italiano`, `norsk`, `Polskie`, `русский язык`, `Romanian`, `svenska`, `українська мова`, `日本語` and `اَلْعَرَبِيَّةُ` thanks to `Crowdin Machine Translation`. + - New translations for `Chinese (Traditional)` thanks to @vongola12324. + - New translations for `Dutch` thanks to @NLxDoDge. + - New translations for `Portuguese` thanks to @MiguelNdeCarvalho. +- Support for touch and smaller screen devices: + - Sidebar is able to be collapsed via a button. It is collapsed by default when screen width is lower than `720px`. + - Modals (Settings, Torrent Details, Add Torrent, etc.) are tuned for smaller screen devices. + - It is now possible to open context (right click) menu on iOS/Safari devices by long pressing the item. + - Drag and drop is now possible for touch devices. You can now adjust the order of columns in Settings on touch devices. + - Widths of columns are now adjustable on touch devices. (condensed view) +- Dark color scheme support: + - Flood now automatically switches between light and dark color scheme based on your system settings. +- XML special chars (`&`, `<`, `>`, `'`, `"`) are properly handled. For instance, escaped chars like `&` will be properly displayed as `&` instead of `&`. File operations on torrent with special chars no longer fail. +- `squashfs` and `tmpfs` mount points are now excluded by default in disk usage. This hopefully makes sure that useless system mounts won't spam the list. +- `More Info` button in expanded view is removed. +- More dependencies are bumped to the latest revisions. + +## [2.0.0] (August 5, 2020) + +- BREAKING CHANGES: + - Bump dependencies to the latest version if possible + - Node 12 or later is now required +- Supports connecting to multiple rtorrent instances (one per user) + - Moved rtorrent configuration to user database + - Prompts user for connection details in UI when can't connect to rtorrent +- Changed `/list/` route to `/overview/` +- Reorganized and renamed component source files +- Removed verbose logging from `HistoryEra` +- Check existing feed items against new download rules +- Switch URL and Label textboxes in Add Feed form to match the Download Rules form +- Rate-limit the SCGI calls to rTorrent + - Sends only one call at a time + - Sends at most one call every 250 miliseconds +- Implement "actity stream" + - The Flood client no longer polls the Flood server on an interval. Instead, + the Flood server polls rTorrent on a more regular interval and emits changes + via an event-stream. This significantly reduces data usage on the Flood client + - Stream covers torrent list, transfer rate summary & history, + torrent taxonomy, and notification count. +- Close event stream after the window/tab has been inactive for 30 seconds +- Refactor development experience, using `Webpack` & `WebpackDevServer` +- Require users to build static assets again +- Simplify peer geo flag handling + - Flag images now serves as static asset +- moveTorrents: Allow hash check to be skipped by user +- Add an option to completely disable users and authentication +- server: Takes baseURI into account for routes and assets +- torrentListPropMap: use d.hashing= instead of d.is_hash_checking= + - Torrents queued for checking are now shown +- sidebar: Add Checking filter view ## [1.0.0] (April 21, 2017) -* First "official" release -* Change log and semver versioning (finally) -* Control basic rTorrent settings via web UI - * Transfer rate limiting - * Connection settings - * Resource utilization -* Add torrents via URLs or files -* User authentication -* UI translations (only en, fr, and nl) -* Custom torrent tags -* Customizable torrent list - * "Expanded" and "condensed" views - * Customizable torrent detail columns -* Basic torrent list filtering (by status, tag, and tracker) -* Auto-download torrents from RSS feeds - -[Unreleased]:https://github.com/Flood-UI/flood/compare/v1.0.0...HEAD -[1.0.0]:https://github.com/Flood-UI/flood/compare/ae520c0a33ffb4ae6f21e47bc6f7e6007dd1e6dc...v1.0.0 + +- First "official" release +- Change log and semver versioning (finally) +- Control basic rTorrent settings via web UI + - Transfer rate limiting + - Connection settings + - Resource utilization +- Add torrents via URLs or files +- User authentication +- UI translations (only en, fr, and nl) +- Custom torrent tags +- Customizable torrent list + - "Expanded" and "condensed" views + - Customizable torrent detail columns +- Basic torrent list filtering (by status, tag, and tracker) +- Auto-download torrents from RSS feeds + +[1.0.0]: https://github.com/Flood-UI/flood/compare/ae520c0a33ffb4ae6f21e47bc6f7e6007dd1e6dc...v1.0.0 +[2.0.0]: https://github.com/jesec/flood/compare/v1.0.0...v2.0.0 +[3.0.0]: https://github.com/jesec/flood/compare/v2.0.0...v3.0.0 +[3.1.0]: https://github.com/jesec/flood/compare/v3.0.0...v3.1.0 +[4.0.0]: https://github.com/jesec/flood/compare/v3.1.0...v4.0.0 +[4.0.1]: https://github.com/jesec/flood/compare/v4.0.0...v4.0.1 +[4.0.2]: https://github.com/jesec/flood/compare/v4.0.1...v4.0.2 +[4.1.0]: https://github.com/jesec/flood/compare/v4.0.2...v4.1.0 +[4.1.1]: https://github.com/jesec/flood/compare/v4.1.0...v4.1.1 +[4.1.2]: https://github.com/jesec/flood/compare/v4.1.1...v4.1.2 +[4.2.0]: https://github.com/jesec/flood/compare/v4.1.2...v4.2.0 +[4.3.0]: https://github.com/jesec/flood/compare/v4.2.0...v4.3.0 +[4.3.1]: https://github.com/jesec/flood/compare/v4.3.0...v4.3.1 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 25e980c39..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,46 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at me@johnfurrow.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/Dockerfile b/Dockerfile index 83fa65f8b..8d4da8065 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,49 +1,51 @@ -ARG NODE_IMAGE=node:12.2-alpine -ARG WORKDIR=/usr/src/app/ - -FROM ${NODE_IMAGE} as nodebuild -ARG WORKDIR - -WORKDIR $WORKDIR - -# Generate node_modules -COPY package.json \ - package-lock.json \ - .babelrc \ - .eslintrc.js \ - .eslintignore \ - .prettierrc \ - ABOUT.md \ - $WORKDIR -RUN apk add --no-cache --virtual=build-dependencies \ - python build-base && \ - npm install && \ - apk del --purge build-dependencies - -# Build static assets and remove devDependencies. -COPY client ./client -COPY server ./server -COPY shared ./shared -COPY scripts ./scripts -COPY config.docker.js ./config.js -RUN npm run build && \ - npm prune --production - -# Now get the clean image without any dependencies and copy compiled app +# WARNING: +# This Dockerfile uses contents of current folder which might contain +# secrets, uncommitted changes or other sensitive information. DO NOT +# publish the result image unless it was composed in a clean environment. + +ARG BUILDPLATFORM=amd64 +ARG NODE_IMAGE=node:alpine + +FROM --platform=$BUILDPLATFORM ${NODE_IMAGE} as nodebuild + +WORKDIR /usr/src/app/ + +# Copy project files +COPY . ./ + +# Install build dependencies +RUN apk --no-cache add \ + python build-base + +# Fetch dependencies from npm +RUN npm ci --no-optional + +# Build package +RUN cp config.cli.js config.js +RUN npm pack + +# Now get the clean Node.js image FROM ${NODE_IMAGE} as flood -ARG WORKDIR -WORKDIR $WORKDIR +# Copy package built +RUN rm -rf /tmp/* +COPY --from=nodebuild /usr/src/app/*.tgz /tmp/ -# Install runtime dependencies. +# Install package and then remove caches +RUN npm i -g /tmp/*.tgz && rm -rf /tmp/* /root/* + +# Install runtime dependencies RUN apk --no-cache add \ mediainfo -COPY --from=nodebuild $WORKDIR $WORKDIR +# Create "download" user +RUN adduser -h /home/download -s /sbin/nologin --disabled-password download + +# Run as "download" user +USER download -# Hints for consumers of the container. +# Expose port 3000 EXPOSE 3000 -VOLUME ["/data"] -# Start application. -CMD [ "npm", "start" ] +# Flood +ENTRYPOINT ["flood", "--host=0.0.0.0"] diff --git a/Dockerfile.release b/Dockerfile.release new file mode 100644 index 000000000..973f6dec5 --- /dev/null +++ b/Dockerfile.release @@ -0,0 +1,21 @@ +# Use node alpine docker image +FROM node:alpine + +# Get the latest release from npm +RUN npm i -g flood --unsafe-perm + +# Install runtime dependencies +RUN apk --no-cache add \ + mediainfo + +# Create "download" user +RUN adduser -h /home/download -s /sbin/nologin --disabled-password download + +# Run as "download" user +USER download + +# Expose port 3000 +EXPOSE 3000 + +# Flood +ENTRYPOINT ["flood", "--host=0.0.0.0"] diff --git a/Dockerfile.rtorrent b/Dockerfile.rtorrent new file mode 100644 index 000000000..f747c95a1 --- /dev/null +++ b/Dockerfile.rtorrent @@ -0,0 +1,17 @@ +ARG FLOOD_VERSION=latest +ARG RTORRENT_VERSION=latest + +FROM jesec/rtorrent:$RTORRENT_VERSION as rtorrent + +FROM jesec/flood:$FLOOD_VERSION as flood + +# Install rTorrent +COPY --from=rtorrent /usr/local/bin/rtorrent /usr/local/bin/ + +# Default rTorrent configuration +# SCGI socket is exposed to $HOME/.local/share/rtorrent/rtorrent.sock +# With "download" user, /home/download/.local/share/rtorrent/rtorrent.sock +COPY --from=rtorrent /etc/rtorrent/rtorrent.rc /etc/rtorrent/ + +# Flood with managed rTorrent daemon +ENTRYPOINT ["flood", "--host=0.0.0.0", "--rtorrent"] diff --git a/README.md b/README.md index c1b04e17b..f0184e49d 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,140 @@ # Flood -![Flood logo](flood.png) +[![Flood logo](https://github.com/jesec/flood/raw/master/flood.svg)](https://flood.js.org) -[![Travis CI build status badge](https://img.shields.io/travis/Flood-UI/flood/master.svg?style=flat-square)](https://travis-ci.org/Flood-UI/flood) [![Discord server badge](https://img.shields.io/discord/418267176873623553.svg?style=flat-square)](https://discord.gg/Z7yR5Uf) +[![Github Actions build status badge](https://github.com/jesec/flood/workflows/Build/badge.svg?branch=master&event=push)](https://github.com/jesec/flood/actions) [![Crowdin](https://badges.crowdin.net/flood/localized.svg)](https://crowdin.com/project/flood) [![Discord server badge](https://img.shields.io/discord/418267176873623553.svg?style=flat-square)](https://discord.gg/Z7yR5Uf) -Flood is a monitoring service for [rTorrent](https://github.com/rakshasa/rtorrent). It's a Node.js service that communicates with rTorrent instances and serves a decent web UI for administration. It's a work-in-progress. +Flood is a monitoring service for various torrent clients. It's a Node.js service that communicates with your favorite torrent client and serves a decent web UI for administration. This project is based on the [original Flood project](https://github.com/Flood-UI/flood). + +#### Supported Clients + +| Client | Support | +| --------------------------------------------------------------- | ------------------------------------ | +| [rTorrent](https://github.com/rakshasa/rtorrent) | Stable and Tested :white_check_mark: | +| [qBittorrent](https://github.com/qbittorrent/qBittorrent) v4.1+ | Experimental :alembic: | +| [Transmission](https://github.com/transmission/transmission) | Experimental :alembic: | #### Feedback -If you have a specific issue or bug, please file a Github issue. Please join the [Flood Discord server](https://discord.gg/Z7yR5Uf) to discuss feature requests and implementation details. +If you have a specific issue or bug, please file a [GitHub issue](https://github.com/jesec/flood/issues). Please join the [Flood Discord server](https://discord.gg/Z7yR5Uf) to discuss feature requests and implementation details. + +#### More Information + +Check out the [Wiki](https://github.com/jesec/flood/wiki) for more information. # Getting started ### Pre-Requisites -1. [rTorrent](https://github.com/rakshasa/rtorrent) needs to be installed and running __with XMLRPC__ configuration. - * For Linux & OS X, check out [rTorrent's installation wiki](https://github.com/rakshasa/rtorrent/wiki/Installing#compilation-help) and/or [this third-party tutorial](https://jes.sc/kb/rTorrent+ruTorrent-Seedbox-Guide.php#Install-Dependencies). When you run `./configure`, be sure to run with the `--with-xmlrpc-c` flag. - * For Windows, try [this guide](https://rtwi.jmk.hu/wiki/rTorrentOnWindows). -2. Install NodeJS version `8` or higher (you might want to manage different Node versions with [nodenv](https://github.com/nodenv/nodenv) or [nvm](https://github.com/creationix/nvm) or [n](https://github.com/tj/n)). -3. Install `node-gyp` pre-requisites, see https://www.npmjs.com/package/node-gyp#installation, ex: `python2`, `make`, `gcc`. +Install [Node.js runtime](https://nodejs.org/). Flood tracks `Current` and provides support to `Active LTS` as well. + +- Debian, Ubuntu and RHEL-based distributions users can install latest `nodejs` from [NodeSource](https://github.com/nodesource/distributions) software repository. +- Windows and MacOS users may use installer. + +**EXPERIMENTAL**: You can download a single-executable build from [Releases](https://github.com/jesec/flood/releases) (or rolling build from [Actions](https://github.com/jesec/flood/actions?query=workflow%3A%22Publish+rolling+build%22)). It bundles `Node.js` and supports `Linux`, `MacOS` and `Windows`. + +### Installation + +`sudo npm i -g flood` or `npx flood` + +Or use `@jesec/flood` for cutting-edge builds. + +Or [build from source](https://github.com/jesec/flood#Building-Flood). + +### Run + +`flood` or `npx flood` if you installed Flood via `npm`. + +`npm run start` if you compiled Flood from source. + +Check [Wiki](https://github.com/jesec/flood/wiki) for how to install Flood as a service. ### Configuration -Copy `config.template.js` to `config.js` and review its comments. **This is required.** +Flood uses a command line configuration interface. -When loading the web interface, you will be prompted to configure the connection to rtorrent. Other configuration options are handled `config.js`. +Run `flood --help`, `npx flood --help` or `npm run start -- --help` to get help about command line arguments. + +If you want to know more about configurations, check [shared/schema/Config.ts](https://github.com/jesec/flood/blob/master/shared/schema/Config.ts). + +When Flood's builtin user management is enabled (default), you will be prompted to configure the connection to torrent client when loading the web interface. **What to configure** -1. Be sure to create a long and unique secret (used to sign [JWT auth tokens](https://github.com/auth0/node-jsonwebtoken)). -3. If you are proxying requests to Flood from your own web server, configure Flood's path from the host at the `baseURI` property. All requests will be prefixed with this value. - * For example, if serving Flood from `https://foo.bar/apps/flood`, you would set `baseURI` to `/apps/flood`. If serving flood from `https://foo.bar`, you do not need to configure `baseURI`. - * [Read more about proxying requests to Flood on the Wiki](https://github.com/Flood-UI/flood/wiki/Using-Flood-behind-a-reverse-proxy), this is a common pain-point for users. - -**Note**: Some of these values are baked into the static assets (like `baseURI`), so changes to this file require recompling static assets. +1. If you are proxying requests to Flood from your own web server, configure Flood's path from the host at the `--baseuri` (or `baseURI`) property. All requests will be prefixed with this value. + - For example, if serving Flood from `https://foo.bar/apps/flood`, you would set `baseURI` to `/apps/flood`. If serving flood from `https://foo.bar`, you do not need to configure `baseURI`. + - Read more about proxying requests to Flood in the [Wiki](https://github.com/jesec/flood/wiki). +1. Check [Wiki](https://github.com/jesec/flood/wiki), especially `Security` sections. + +### Upgrade + +Run the installation command again. + +### Troubleshooting + +- Flood and filesystem: + - Flood server performs file operations itself. As such, Flood needs to have permissions/access to the files. + - Flood only uses the path provided by the torrent client so it needs to have the same filesystem context as the torrent client. If a file is "/path/to/a/file" to the torrent client, it has to be "/path/to/a/file" to Flood in order to get file operations working. It can't be "/mnt/some/different/path/file". +- rTorrent: + - Linux or MacOS users can use package managers such as `apt`, `yum`, `pacman`, `brew` of the platform to install rTorrent. Alternatively, download latest static binary (available for `linux/amd64` and `linux/arm64`) from [Github Actions](https://github.com/jesec/rtorrent/actions?query=workflow%3A%22Publish+rolling+build%22). + - Windows users can use [Cygwin](https://www.cygwin.com/) to install rTorrent. + - [Compile](https://github.com/rakshasa/rtorrent/wiki/Installing): XMLRPC support flag (`--with-xmlrpc-c`) is required during compilation. +- Ask for help in the [Flood Discord server](https://discord.gg/Z7yR5Uf). + +### Docker + +`docker run -it jesec/flood --help` + +Or `jesec/flood:master` for cutting-edge builds. + +To upgrade, `docker pull jesec/flood`. + +Note that you have to let Docker know which port should be exposed (e.g. `-p 3000:3000`) and folder mapping (e.g. `-v /data:/data`). + +Don't forget to pay attention to `flood`'s arguments like `--port` and `--allowedpath`. + +Checkout [Dockerfile.rtorrent](https://github.com/jesec/flood/blob/master/Dockerfile.rtorrent) for a simple example of Flood-rTorrent Docker integration. + +Filesystem parts in [Troubleshooting](https://github.com/jesec/flood#troubleshooting) are especially important for containers. + +## Building Flood + +### Clone from repository + +`git clone https://github.com/jesec/flood.git` ### Compiling assets and starting the server From the root of the Flood directory... -1. Run `npm install` if you haven't already or if you've pulled changes. -2. Run `npm run build`. -3. Run `npm start`. -Access the UI in your browser. With default settings, go to `http://localhost:3000`. You can configure the port in `config.js`. +1. Run `npm install`. +1. Run `npm run build`. +1. Run `npm start`. + +Access the UI in your browser. With default settings, go to `http://localhost:3000`. You can configure the port via `--port` argument. -### Updating +**Notes** + +- When you use `npm run start` to execute Flood, you have to pass command line arguments after `--`. For example, `npm run start -- --host 0.0.0.0 --port 8080`. This applies to any `npm run` (e.g. `start:development:client`). -I've been bad about cutting actual releases, so check this repo for recent commits. +### Updating 1. To update, run `git pull` in this repository's directory. -1. Check `config.template.js` for configuration changes that you may wish to incoporate in your `config.js`. 1. Kill the currently running Flood server. 1. Run `npm install` to update dependencies. 1. Run `npm run build` to transpile and bundle static assets. 1. Start the Flood server with `npm start`. -### Troubleshooting - -* Ubuntu users may need to install `nodejs-legacy` (`sudo apt-get install nodejs-legacy`) for dependencies to install successfully. You can read more on [this Stack Overflow post](http://stackoverflow.com/questions/21168141/cannot-install-packages-using-node-package-manager-in-ubuntu). -* Ask for help in the [Flood Discord server](https://discord.gg/Z7yR5Uf). - ### Local Development 1. Run `npm install`. -2. Run `npm run start:development:server` and `npm run start:development:client` in separate terminal instances. - * `npm run start:development:server` uses [nodemon](https://github.com/remy/nodemon) to watch for changes to the server-side JavaScript. - * `npm run start:development:client` watches for changes in the client-side source. -3. Access the UI in your browser. Defaults to `localhost:4200`. +1. Run `npm run start:development:server` and `npm run start:development:client` in separate terminal instances. + - `npm run start:development:server` uses [ts-node-dev](https://www.npmjs.com/package/ts-node-dev) to watch for changes to the server-side source. Or open the folder with VS code and then `Run -> Start Debugging`. You may use a [Javascript IDE](https://code.visualstudio.com/) to debug server codes. + - `npm run start:development:client` watches for changes in the client-side source. Access the UI in your browser. Defaults to `localhost:4200`. You may use browser's [DevTools](https://developers.google.com/web/tools/chrome-devtools) to debug client codes. + +`--help --show-hidden` shows advanced arguments. + +`--proxy` proxies requests from a development client to a URL of your choice (usually URL to a Flood server). It is useful when you wish to do development on the frontend but not the backend. Or when the frontend and backend are being developed on different hosts. ### Environment Variables @@ -74,15 +142,7 @@ I've been bad about cutting actual releases, so check this repo for recent commi 1. `DEV_SERVER_HOST`: webpackDevServer's host, used when developing Flood. Defaults to `0.0.0.0`. 1. `DEV_SERVER_HTTPS`: webpackDevServer's protocol, used when developing Flood. Defaults to `http`. -### Running with Docker - -1. `docker build -t rtorrent-flood .` -2. `docker run --name rtorrent-flood -e RTORRENT_SCGI_HOST=w.x.y.z -p 3000:3000 rtorrent-flood` -3. Other supported environment variables: - * `FLOOD_BASE_URI` - * `FLOOD_SECRET` - * `FLOOD_ENABLE_SSL` - -The docker container includes a volume at `/data`, which is where the database will be located. Additionally, you can place your SSL files there, `/data/flood_ssl.key` and `/data/flood_ssl.cert`. Set `FLOOD_ENABLE_SSL` to `true` to enable their use if present. Additionally, a local rtorrent socket file located at `/data/rtorrent.sock` can be used if `RTORRENT_SOCK` is set to `true`. The location of the socket file can be overrided by setting `RTORRENT_SOCK_PATH` to the path of the socket. +### Building Docker -Check out the [Wiki](https://github.com/Flood-UI/flood/wiki/Docker) for more information. +1. `docker build --pull --rm -f Dockerfile -t flood:latest .` +1. `docker run -it flood --help` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..863b69315 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security + +## Supported Versions + +| Version | Supported | +| ------------------------ | ------------------ | +| HEAD of master | :white_check_mark: | +| Latest released revision | :white_check_mark: | +| All old revisions | :x: | + +Flood does NOT provide LTS support. Older revisions are deprecated as soon as a newer revision is released. + +Generally only the latest revision (`HEAD of master`) is supported. However, [issue tracker](https://github.com/jesec/flood/issues) is still open to reports from users of the `Latest released revision`. In rare circumstances, if there is a vulnerability that requires urgent attention, and, if the `HEAD of master` is occupied with changes which maintainers are not comfortable to release, relevant changes may be backported to the `Latest released revision` to release a patch revision. + +You are advised to upgrade to the latest release as soon as possible. + +## Reporting a Vulnerability + +If you discover a security vulnerability within Flood, please send an e-mail to maintainer. + +Suggestions for general security enhancements and/or mitigations shall be reported to [issue tracker](https://github.com/jesec/flood/issues). + +If you are unsure about the severity, send an email first. + +## More information + +Check out the [Wiki](https://github.com/jesec/flood/wiki) for more information. diff --git a/client/.eslintrc.js b/client/.eslintrc.js deleted file mode 100644 index 91f1aa3ef..000000000 --- a/client/.eslintrc.js +++ /dev/null @@ -1,72 +0,0 @@ -const path = require('path'); - -module.exports = { - parser: 'babel-eslint', - extends: ['plugin:@typescript-eslint/recommended', 'prettier', 'prettier/@typescript-eslint'], - env: { - browser: 1, - node: 0, - }, - globals: { - global: 'writable', - process: 'writable', - window: 'writable', - }, - plugins: ['import'], - rules: { - '@typescript-eslint/no-var-requires': 0, - // This is enabled to allow BEM-style classnames to be referenced JS - // Remvoe when BEM-style classnames are converted to locally-scoped - // class names - '@typescript-eslint/camelcase': ['error', {allow: [/[^\s]__[^\s]{1,}$/]}], - camelcase: 0, - // TODO: Enable a11y features - 'jsx-a11y/click-events-have-key-events': 0, - 'jsx-a11y/label-has-associated-control': 0, - 'jsx-a11y/label-has-for': 0, - 'jsx-a11y/mouse-events-have-key-events': 0, - 'jsx-a11y/no-noninteractive-element-interactions': 0, - 'jsx-a11y/no-static-element-interactions': 0, - 'no-console': [2, {allow: ['warn', 'error']}], - 'react/button-has-type': 0, - 'react/default-props-match-prop-types': 0, - 'react/destructuring-assignment': 0, - 'react/forbid-prop-types': 0, - 'react/jsx-closing-bracket-location': 0, - 'react/jsx-filename-extension': [1, {extensions: ['.js', '.tsx']}], - 'react/jsx-one-expression-per-line': 0, - 'react/jsx-wrap-multilines': 0, - 'react/no-unescaped-entities': ['error', {forbid: ['>', '}']}], - 'react/no-unused-prop-types': 0, - 'react/prefer-stateless-function': 0, - 'react/prop-types': 0, - 'react/require-default-props': 0, - 'react/sort-comp': [ - 2, - { - order: ['static-methods', 'instance-variables', 'lifecycle', 'everything-else', 'rendering'], - groups: { - rendering: ['/^render.+$/', 'render'], - }, - }, - ], - }, - settings: { - 'import/resolver': { - webpack: { - config: path.join(__dirname, 'config/webpack.config.dev.js'), - }, - }, - }, - overrides: [ - { - files: ['*.ts', '*.tsx', '**/*.ts', '**/*.tsx'], - parser: '@typescript-eslint/parser', - plugins: ['import', '@typescript-eslint/eslint-plugin'], - rules: { - 'no-unused-vars': 0, - '@typescript-eslint/no-unused-vars': 1, - }, - }, - ], -}; diff --git a/client/config/.eslintrc.js b/client/config/.eslintrc.js deleted file mode 100644 index b804850f7..000000000 --- a/client/config/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - env: { - browser: 0, - node: 1, - }, - rules: { - 'no-console': 0, - 'global-require': 0, - }, -}; diff --git a/client/config/env.js b/client/config/env.js deleted file mode 100644 index 09fca14a8..000000000 --- a/client/config/env.js +++ /dev/null @@ -1,56 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const paths = require('./paths'); -const userConfig = require('../../config'); - -// Make sure that including paths.js after env.js will read .env variables. -delete require.cache[require.resolve('./paths')]; - -// We support resolving modules according to `NODE_PATH`. -// This lets you use absolute paths in imports inside large monorepos: -// https://github.com/facebookincubator/create-react-app/issues/253. -// It works similar to `NODE_PATH` in Node itself: -// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders -// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. -// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. -// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421 -// We also resolve them to make sure all tools using them work consistently. -const appDirectory = fs.realpathSync(process.cwd()); -process.env.NODE_PATH = (process.env.NODE_PATH || '') - .split(path.delimiter) - .filter(folder => folder && !path.isAbsolute(folder)) - .map(folder => path.resolve(appDirectory, folder)) - .join(path.delimiter); - -// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be -// injected into the application via DefinePlugin in Webpack configuration. -const REACT_APP = /^REACT_APP_/i; -const environment = process.env.NODE_ENV || 'development'; - -function getClientEnvironment() { - const raw = Object.keys(process.env) - .filter(key => REACT_APP.test(key)) - .reduce( - (env, key) => { - env[key] = process.env[key]; - return env; - }, - { - NODE_ENV: environment, - BASE_URI: environment !== 'development' ? paths.servedPath : '', - POLL_INTERVAL: userConfig.torrentClientPollInterval, - }, - ); - // Stringify all values so we can feed into Webpack DefinePlugin - const stringified = { - 'process.env': Object.keys(raw).reduce((env, key) => { - env[key] = JSON.stringify(raw[key]); - return env; - }, {}), - }; - - return {raw, stringified}; -} - -module.exports = getClientEnvironment; diff --git a/client/config/jest/cssTransform.js b/client/config/jest/cssTransform.js deleted file mode 100644 index 56746693d..000000000 --- a/client/config/jest/cssTransform.js +++ /dev/null @@ -1,12 +0,0 @@ -// This is a custom Jest transformer turning style imports into empty objects. -// http://facebook.github.io/jest/docs/tutorial-webpack.html - -module.exports = { - process() { - return 'module.exports = {};'; - }, - getCacheKey() { - // The output is always the same. - return 'cssTransform'; - }, -}; diff --git a/client/config/jest/fileTransform.js b/client/config/jest/fileTransform.js deleted file mode 100644 index 3166508c5..000000000 --- a/client/config/jest/fileTransform.js +++ /dev/null @@ -1,10 +0,0 @@ -const path = require('path'); - -// This is a custom Jest transformer turning file imports into filenames. -// http://facebook.github.io/jest/docs/tutorial-webpack.html - -module.exports = { - process(src, filename) { - return `module.exports = ${JSON.stringify(path.basename(filename))};`; - }, -}; diff --git a/client/config/paths.js b/client/config/paths.js deleted file mode 100644 index 856f2cecb..000000000 --- a/client/config/paths.js +++ /dev/null @@ -1,31 +0,0 @@ -const path = require('path'); -const fs = require('fs'); -const userConfig = require('../../config'); - -// Make sure any symlinks in the project folder are resolved: -// https://github.com/facebookincubator/create-react-app/issues/637 -const appDirectory = fs.realpathSync(process.cwd()); -const resolveApp = relativePath => path.resolve(appDirectory, relativePath); -const ensureSlash = (questionablePath, needsSlash) => { - const hasSlash = questionablePath.endsWith('/'); - if (hasSlash && !needsSlash) { - return questionablePath.substr(questionablePath, questionablePath.length - 1); - } - if (!hasSlash && needsSlash) { - return `${questionablePath}/`; - } - return questionablePath; -}; - -module.exports = { - appBuild: resolveApp('server/assets'), - appPublic: resolveApp('client/src/public/'), - appHtml: resolveApp('client/src/index.html'), - appIndex: resolveApp('client/src/javascript/app.tsx'), - appPackageJson: resolveApp('package.json'), - appSrc: resolveApp('./'), - clientSrc: resolveApp('client/src'), - testsSetup: resolveApp('tests/setupTests.js'), - appNodeModules: resolveApp('node_modules'), - servedPath: ensureSlash(userConfig.baseURI || '/', true), -}; diff --git a/client/config/polyfills.js b/client/config/polyfills.js deleted file mode 100644 index 2a5c9712b..000000000 --- a/client/config/polyfills.js +++ /dev/null @@ -1,11 +0,0 @@ -if (typeof Promise === 'undefined') { - // Rejection tracking prevents a common issue where React gets into an - // inconsistent state due to an error, but it gets swallowed by a Promise, - // and the user has no idea what causes React's erratic future behavior. - require('promise/lib/rejection-tracking').enable(); - window.Promise = require('promise/lib/es6-extensions.js'); -} - -// Object.assign() is commonly used with React. -// It will use the native implementation if it's present and isn't buggy. -Object.assign = require('object-assign'); diff --git a/client/config/webpack.config.dev.js b/client/config/webpack.config.dev.js index f0c7082b8..8cd5e659d 100644 --- a/client/config/webpack.config.dev.js +++ b/client/config/webpack.config.dev.js @@ -1,34 +1,17 @@ -const autoprefixer = require('autoprefixer'); const path = require('path'); const webpack = require('webpack'); +const ESLintPlugin = require('eslint-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); -const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); +const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); -const eslintFormatter = require('react-dev-utils/eslintFormatter'); -const getClientEnvironment = require('./env'); -const paths = require('./paths'); - -const env = getClientEnvironment(); +const WebpackBar = require('webpackbar'); +const paths = require('../../shared/config/paths'); module.exports = { - mode: process.env.NODE_ENV, + mode: 'development', module: { rules: [ - { - enforce: 'pre', - test: /\.(ts|js)x?$/, - exclude: /node_modules/, - use: [ - { - loader: 'eslint-loader', - options: { - formatter: eslintFormatter, - emitWarning: true, - }, - }, - ], - }, { test: /\.(ts|js)x?$/, exclude: /node_modules/, @@ -37,6 +20,7 @@ module.exports = { loader: 'babel-loader', options: { babelrc: true, + plugins: [require.resolve('react-refresh/babel')], }, }, ], @@ -58,23 +42,6 @@ module.exports = { }, }, }, - { - loader: require.resolve('../scripts/typed-css-modules-loader'), - }, - { - loader: 'postcss-loader', - options: { - sourceMap: true, - // Necessary for external CSS imports to work - // https://github.com/facebookincubator/create-react-app/issues/2677 - ident: 'postcss', - plugins: () => [ - autoprefixer({ - browsers: ['>1%'], - }), - ], - }, - }, { loader: 'sass-loader', options: { @@ -84,45 +51,18 @@ module.exports = { ], }, { + exclude: [/node_modules/], test: /\.(ts|js)x?$/, use: ['source-map-loader'], enforce: 'pre', }, { - enforce: 'pre', - test: /\.svg$/, - issuer: /\.(ts|js)x?$/, - use: [ - { - loader: 'babel-loader', - options: { - babelrc: true, - }, - }, - { - loader: 'svg-sprite-loader', - options: { - runtimeGenerator: require.resolve('../scripts/svg-react-component-generator'), - runtimeOptions: { - iconModule: require.resolve('../src/javascript/components/general/Icon.tsx'), - }, - }, - }, - ], + test: /\.md$/, + loader: 'frontmatter-markdown-loader', + options: {mode: ['react-component']}, }, { - exclude: [ - /\.html$/, - /\.(js|jsx|ts|tsx)$/, - /\.css$/, - /\.scss$/, - /\.json$/, - /\.bmp$/, - /\.gif$/, - /\.jpe?g$/, - /\.png$/, - /\.svg$/, - ], + test: [/\.woff2$/], loader: require.resolve('file-loader'), options: { name: 'static/media/[name].[hash:8].[ext]', @@ -151,14 +91,12 @@ module.exports = { }, entry: paths.appIndex, resolve: { - extensions: ['*', '.js', '.jsx', '.ts', '.tsx', '.json'], + extensions: ['.cjs', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'], alias: { '@shared': path.resolve('./shared'), }, }, output: { - // Next line is not used in dev but WebpackDevServer crashes without it: - path: paths.appBuild, // Add /* filename */ comments to generated require()s in the output. pathinfo: true, // This does not produce a real file. It's just the virtual path that is @@ -167,31 +105,21 @@ module.exports = { filename: 'static/js/bundle.js', // There are also additional JS chunk files if you use code splitting. chunkFilename: 'static/js/[name].chunk.js', - // This is the URL that app is served from. We use "/" in development. - // Webpack uses `publicPath` to determine where the app is being served from. - // In development, we always serve from the root. This makes config easier. - publicPath: '/', - // Point sourcemap entries to original disk location (format as URL on Windows) - devtoolModuleFilenameTemplate: info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'), }, plugins: [ - // Makes some environment variables available in index.html. - // The base URI is available as %BASE_URI% in index.html, e.g.: - // - // In development, this will be an empty string. - new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw), + new ESLintPlugin({ + extensions: ['js', 'jsx', 'ts', 'tsx'], + emitWarning: true, + threads: true, + }), // Generates an `index.html` file with the Flood + - +
diff --git a/client/src/javascript/actions/AuthActions.js b/client/src/javascript/actions/AuthActions.js deleted file mode 100644 index 41659efab..000000000 --- a/client/src/javascript/actions/AuthActions.js +++ /dev/null @@ -1,175 +0,0 @@ -import axios from 'axios'; - -import ActionTypes from '../constants/ActionTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import ClientActions from './ClientActions'; -import ConfigStore from '../stores/ConfigStore'; -import FloodActions from './FloodActions'; -import SettingsActions from './SettingsActions'; - -const baseURI = ConfigStore.getBaseURI(); - -const AuthActions = { - authenticate: credentials => - axios - .post(`${baseURI}auth/authenticate`, credentials) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_LOGIN_SUCCESS, - data, - }); - }, - error => { - // TODO: Handle errors consistently in API, then create a client-side class to get meaningful messages from - // server's response. - let errorMessage; - - if (error.response) { - errorMessage = error.response.data.message; - } else if (error.message) { - errorMessage = error.message; - } else { - errorMessage = 'An unknown error occurred.'; - } - - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_LOGIN_ERROR, - error: errorMessage, - }); - - throw new Error(errorMessage); - }, - ) - .then(() => { - return Promise.all([ - ClientActions.fetchSettings(), - SettingsActions.fetchSettings(), - FloodActions.restartActivityStream(), - ]); - }), - - createUser: credentials => - axios - .put(`${baseURI}auth/users`, credentials) - .then((json = {}) => json.data) - .then(data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_CREATE_USER_SUCCESS, - data, - }); - }), - - updateUser: (username, connectionSettings) => { - const requestPayload = {}; - - if (connectionSettings.connectionType === 'socket') { - requestPayload.socketPath = connectionSettings.rtorrentSocketPath; - } else { - requestPayload.port = connectionSettings.rtorrentPort; - requestPayload.host = connectionSettings.rtorrentHost; - } - - return axios - .patch(`${baseURI}auth/users/${encodeURIComponent(username)}`, requestPayload) - .then((json = {}) => json.data); - }, - - deleteUser: username => - axios - .delete(`${baseURI}auth/users/${encodeURIComponent(username)}`) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_DELETE_USER_SUCCESS, - data: { - username, - ...data, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_DELETE_USER_ERROR, - error: { - username, - ...error, - }, - }); - }, - ), - - fetchUsers: () => - axios - .get(`${baseURI}auth/users`) - .then((json = {}) => json.data) - .then(data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_LIST_USERS_SUCCESS, - data, - }); - }), - - logout: () => - axios.get(`${baseURI}auth/logout`).then( - () => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_LOGOUT_SUCCESS, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_LOGOUT_ERROR, - error, - }); - }, - ), - - register: credentials => - axios - .post(`${baseURI}auth/register`, credentials) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_REGISTER_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_REGISTER_ERROR, - error: error.response.data.message, - }); - }, - ), - - verify: () => - axios - .get(`${baseURI}auth/verify?${Date.now()}`) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_VERIFY_SUCCESS, - data, - }); - - return Promise.all([ClientActions.fetchSettings(), SettingsActions.fetchSettings()]).then(() => { - return data; - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.AUTH_VERIFY_ERROR, - error, - }); - - throw error; - }, - ), -}; - -export default AuthActions; diff --git a/client/src/javascript/actions/AuthActions.ts b/client/src/javascript/actions/AuthActions.ts new file mode 100644 index 000000000..5efcaf76d --- /dev/null +++ b/client/src/javascript/actions/AuthActions.ts @@ -0,0 +1,140 @@ +import axios, {AxiosError, AxiosResponse} from 'axios'; + +import type { + AuthAuthenticationOptions, + AuthRegistrationOptions, + AuthUpdateUserOptions, + AuthVerificationResponse, +} from '@shared/schema/api/auth'; +import type {Credentials} from '@shared/schema/Auth'; + +import AuthStore from '../stores/AuthStore'; +import ClientActions from './ClientActions'; +import ConfigStore from '../stores/ConfigStore'; +import FloodActions from './FloodActions'; +import SettingActions from './SettingActions'; + +const {baseURI} = ConfigStore; + +const AuthActions = { + authenticate: (options: AuthAuthenticationOptions) => + axios + .post(`${baseURI}api/auth/authenticate`, options) + .then((json) => json.data) + .then( + (data) => { + AuthStore.handleLoginSuccess(data); + }, + (error) => { + // TODO: Handle errors consistently in API, then create a client-side class to get meaningful messages from + // server's response. + let errorMessage; + + if (error.response && error.response.data.message != null) { + errorMessage = error.response.data.message; + } else if (error.message) { + errorMessage = error.message; + } else { + errorMessage = 'An unknown error occurred.'; + } + + AuthStore.handleLoginError(); + + throw new Error(errorMessage); + }, + ) + .then(() => + Promise.all([ + ClientActions.fetchSettings(), + SettingActions.fetchSettings(), + FloodActions.restartActivityStream(), + ]), + ), + + createUser: (options: AuthRegistrationOptions) => + axios + .post(`${baseURI}api/auth/register?cookie=false`, options) + .then((json) => json.data) + .then((data) => { + AuthStore.handleCreateUserSuccess(data); + }), + + updateUser: (username: Credentials['username'], options: AuthUpdateUserOptions) => + axios.patch(`${baseURI}api/auth/users/${encodeURIComponent(username)}`, options).then((json) => json.data), + + deleteUser: (username: Credentials['username']) => + axios + .delete(`${baseURI}api/auth/users/${encodeURIComponent(username)}`) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + // do nothing. + }, + ), + + fetchUsers: () => + axios + .get(`${baseURI}api/auth/users`) + .then((json) => json.data) + .then((data) => { + AuthStore.handleListUsersSuccess(data); + }), + + logout: () => + axios.get(`${baseURI}api/auth/logout`).then( + () => { + // do nothing. + }, + () => { + // do nothing. + }, + ), + + register: (options: AuthRegistrationOptions) => + axios + .post(`${baseURI}api/auth/register`, options) + .then((json) => json.data) + .then( + (data) => { + AuthStore.handleRegisterSuccess(data); + }, + (error: AxiosError) => { + throw error; + }, + ), + + verify: () => + axios + .get(`${baseURI}api/auth/verify?${Date.now()}`) + .then( + (res: AxiosResponse) => { + if (res.data.configs != null) { + ConfigStore.handlePreloadConfigs(res.data.configs); + } + return res.data; + }, + (error: AxiosError) => { + if (error.response?.data?.configs != null) { + ConfigStore.handlePreloadConfigs(error.response.data.configs); + } + throw error; + }, + ) + .then( + (data: AuthVerificationResponse) => { + AuthStore.handleAuthVerificationSuccess(data); + + return Promise.all([ClientActions.fetchSettings(), SettingActions.fetchSettings()]).then(() => data); + }, + (error) => { + AuthStore.handleAuthVerificationError(); + + throw error; + }, + ), +} as const; + +export default AuthActions; diff --git a/client/src/javascript/actions/ClientActions.js b/client/src/javascript/actions/ClientActions.js deleted file mode 100644 index 00cd7585d..000000000 --- a/client/src/javascript/actions/ClientActions.js +++ /dev/null @@ -1,104 +0,0 @@ -import axios from 'axios'; - -import ActionTypes from '../constants/ActionTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import ConfigStore from '../stores/ConfigStore'; - -const baseURI = ConfigStore.getBaseURI(); - -const ClientActions = { - fetchSettings: property => - axios - .get(`${baseURI}api/client/settings`, {params: {property}}) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SETTINGS_FETCH_REQUEST_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SETTINGS_FETCH_REQUEST_ERROR, - error, - }); - }, - ), - - saveSettings: (settings, options) => - axios - .patch(`${baseURI}api/client/settings`, settings) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SETTINGS_SAVE_SUCCESS, - data, - options, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SETTINGS_SAVE_ERROR, - error, - options, - }); - }, - ), - - setThrottle: (direction, throttle) => - axios - .put(`${baseURI}api/client/settings/speed-limits`, { - direction, - throttle, - }) - .then((json = {}) => json.data) - .then( - transferData => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SET_THROTTLE_SUCCESS, - data: { - transferData, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SET_THROTTLE_ERROR, - data: { - error, - }, - }); - }, - ), - - testClientConnectionSettings: connectionSettings => { - const requestPayload = { - host: connectionSettings.rtorrentHost, - port: connectionSettings.rtorrentPort, - socketPath: connectionSettings.rtorrentSocketPath, - }; - - return axios.post(`${baseURI}api/client/connection-test`, requestPayload).then((json = {}) => json.data); - }, - - testConnection: () => - axios - .get(`${baseURI}api/client/connection-test`) - .then((json = {}) => json.data) - .then( - () => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_CONNECTION_TEST_SUCCESS, - }); - }, - () => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_CONNECTION_TEST_ERROR, - }); - }, - ), -}; - -export default ClientActions; diff --git a/client/src/javascript/actions/ClientActions.ts b/client/src/javascript/actions/ClientActions.ts new file mode 100644 index 000000000..d5aaca744 --- /dev/null +++ b/client/src/javascript/actions/ClientActions.ts @@ -0,0 +1,72 @@ +import axios from 'axios'; + +import type {ClientSetting, ClientSettings} from '@shared/types/ClientSettings'; +import type {SetClientSettingsOptions} from '@shared/types/api/client'; + +import ConfigStore from '../stores/ConfigStore'; +import SettingStore from '../stores/SettingStore'; +import AlertStore from '../stores/AlertStore'; + +const {baseURI} = ConfigStore; + +const ClientActions = { + fetchSettings: async (): Promise => + axios + .get(`${baseURI}api/client/settings`) + .then((json) => json.data) + .then( + (data) => { + SettingStore.handleClientSettingsFetchSuccess(data); + }, + () => { + // do nothing. + }, + ), + + saveSettings: async (settings: SetClientSettingsOptions, options?: {alert?: boolean}): Promise => { + if (Object.keys(settings).length > 0) { + SettingStore.saveClientSettings(settings); + + let err = false; + await axios + .patch(`${baseURI}api/client/settings`, settings) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + err = true; + }, + ); + + if (options?.alert) { + // TODO: More precise error message. + AlertStore.add( + err + ? { + id: 'general.error.unknown', + type: 'error', + } + : { + id: 'alert.settings.saved', + type: 'success', + }, + ); + } + } + }, + + saveSetting: async (property: T, data: ClientSettings[T]): Promise => + ClientActions.saveSettings({[property]: data}), + + testConnection: async (): Promise => + axios + .get(`${baseURI}api/client/connection-test`) + .then((json) => json.data) + .then(() => { + // do nothing. + }), +} as const; + +export default ClientActions; diff --git a/client/src/javascript/actions/FeedActions.ts b/client/src/javascript/actions/FeedActions.ts new file mode 100644 index 000000000..9911c5bbe --- /dev/null +++ b/client/src/javascript/actions/FeedActions.ts @@ -0,0 +1,94 @@ +import axios from 'axios'; + +import type {AddFeedOptions, AddRuleOptions, ModifyFeedOptions} from '@shared/types/api/feed-monitor'; + +import ConfigStore from '../stores/ConfigStore'; +import FeedStore from '../stores/FeedStore'; + +const {baseURI} = ConfigStore; + +const FeedActions = { + addFeed: (options: AddFeedOptions) => + axios + .put(`${baseURI}api/feed-monitor/feeds`, options) + .then((json) => json.data) + .then( + () => { + FeedActions.fetchFeedMonitors(); + }, + () => { + // do nothing. + }, + ), + + modifyFeed: (id: string, options: ModifyFeedOptions) => + axios + .patch(`${baseURI}api/feed-monitor/feeds/${id}`, options) + .then((json) => json.data) + .then( + () => { + FeedActions.fetchFeedMonitors(); + }, + () => { + // do nothing. + }, + ), + + addRule: (options: AddRuleOptions) => + axios + .put(`${baseURI}api/feed-monitor/rules`, options) + .then((json) => json.data) + .then( + () => { + FeedActions.fetchFeedMonitors(); + }, + () => { + // do nothing. + }, + ), + + fetchFeedMonitors: () => + axios + .get(`${baseURI}api/feed-monitor`) + .then((json) => json.data) + .then( + (data) => { + FeedStore.handleFeedMonitorsFetchSuccess(data); + }, + () => { + // do nothing. + }, + ), + + fetchItems: ({id, search}: {id: string; search: string}) => + axios + .get(`${baseURI}api/feed-monitor/feeds/${id}/items`, { + params: { + search, + }, + }) + .then((json) => json.data) + .then( + (data) => { + FeedStore.handleItemsFetchSuccess(data); + }, + () => { + // do nothing. + }, + ), + + removeFeedMonitor: (id: string) => + axios + .delete(`${baseURI}api/feed-monitor/${id}`) + .then((json) => json.data) + .then( + () => { + FeedActions.fetchFeedMonitors(); + }, + () => { + // do nothing. + }, + ), +} as const; + +export default FeedActions; diff --git a/client/src/javascript/actions/FloodActions.js b/client/src/javascript/actions/FloodActions.js deleted file mode 100644 index f6ed73719..000000000 --- a/client/src/javascript/actions/FloodActions.js +++ /dev/null @@ -1,301 +0,0 @@ -import axios from 'axios'; -import historySnapshotTypes from '@shared/constants/historySnapshotTypes'; -import serverEventTypes from '@shared/constants/serverEventTypes'; - -import AppDispatcher from '../dispatcher/AppDispatcher'; -import ActionTypes from '../constants/ActionTypes'; -import ConfigStore from '../stores/ConfigStore'; - -const baseURI = ConfigStore.getBaseURI(); - -let activityStreamEventSource = null; -let lastActivityStreamOptions; -let visibilityChangeTimeout = null; - -const FloodActions = { - clearNotifications: options => - axios - .delete(`${baseURI}api/notifications`) - .then((json = {}) => json.data) - .then( - (response = {}) => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.FLOOD_CLEAR_NOTIFICATIONS_SUCCESS, - data: { - ...response, - ...options, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.FLOOD_CLEAR_NOTIFICATIONS_ERROR, - data: { - error, - }, - }); - }, - ), - - closeActivityStream() { - activityStreamEventSource.close(); - - activityStreamEventSource.removeEventListener( - serverEventTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE, - this.handleClientConnectivityStatusChange, - ); - - activityStreamEventSource.removeEventListener(serverEventTypes.DISK_USAGE_CHANGE, this.handleDiskUsageChange); - - activityStreamEventSource.removeEventListener( - serverEventTypes.NOTIFICATION_COUNT_CHANGE, - this.handleNotificationCountChange, - ); - - activityStreamEventSource.removeEventListener(serverEventTypes.TAXONOMY_DIFF_CHANGE, this.handleTaxonomyDiffChange); - - activityStreamEventSource.removeEventListener(serverEventTypes.TAXONOMY_FULL_UPDATE, this.handleTaxonomyFullUpdate); - - activityStreamEventSource.removeEventListener( - serverEventTypes.TORRENT_LIST_DIFF_CHANGE, - this.handleTorrentListDiffChange, - ); - - activityStreamEventSource.removeEventListener( - serverEventTypes.TORRENT_LIST_FULL_UPDATE, - this.handleTorrentListFullUpdate, - ); - - activityStreamEventSource.removeEventListener( - serverEventTypes.TRANSFER_SUMMARY_DIFF_CHANGE, - this.handleTransferSummaryDiffChange, - ); - - activityStreamEventSource.removeEventListener( - serverEventTypes.TRANSFER_SUMMARY_FULL_UPDATE, - this.handleTransferSummaryFullUpdate, - ); - - activityStreamEventSource.removeEventListener( - serverEventTypes.TRANSFER_HISTORY_FULL_UPDATE, - this.handleTransferHistoryFullUpdate, - ); - - activityStreamEventSource = null; - }, - - fetchDirectoryList: (options = {}) => - axios - .get(`${baseURI}api/directory-list`, { - params: options, - }) - .then((json = {}) => json.data) - .then(response => { - return { - ...options, - ...response, - }; - }), - - fetchMediainfo: options => - axios - .get(`${baseURI}api/mediainfo`, { - params: { - hash: options.hash, - }, - }) - .then((json = {}) => json.data) - .then(response => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.FLOOD_FETCH_MEDIAINFO_SUCCESS, - data: { - ...response, - ...options, - }, - }); - }), - - fetchNotifications: options => - axios - .get(`${baseURI}api/notifications`, { - params: { - limit: options.limit, - start: options.start, - }, - }) - .then((json = {}) => json.data) - .then( - response => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.FLOOD_FETCH_NOTIFICATIONS_SUCCESS, - data: { - ...response, - ...options, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.FLOOD_FETCH_NOTIFICATIONS_ERROR, - data: { - error, - }, - }); - }, - ), - - handleClientConnectivityStatusChange(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE, - data: JSON.parse(event.data), - }); - }, - handleDiskUsageChange(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.DISK_USAGE_CHANGE, - data: JSON.parse(event.data), - }); - }, - handleNotificationCountChange(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.NOTIFICATION_COUNT_CHANGE, - data: JSON.parse(event.data), - }); - }, - - handleTorrentListDiffChange(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.TORRENT_LIST_DIFF_CHANGE, - data: JSON.parse(event.data), - }); - }, - - handleTorrentListFullUpdate(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.TORRENT_LIST_FULL_UPDATE, - data: JSON.parse(event.data), - }); - }, - - handleTaxonomyDiffChange(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.TAXONOMY_DIFF_CHANGE, - data: JSON.parse(event.data), - }); - }, - - handleTaxonomyFullUpdate(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.TAXONOMY_FULL_UPDATE, - data: JSON.parse(event.data), - }); - }, - - handleTransferSummaryDiffChange(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.TRANSFER_SUMMARY_DIFF_CHANGE, - data: JSON.parse(event.data), - }); - }, - - handleTransferSummaryFullUpdate(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.TRANSFER_SUMMARY_FULL_UPDATE, - data: JSON.parse(event.data), - }); - }, - - handleTransferHistoryFullUpdate(event) { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.TRANSFER_HISTORY_FULL_UPDATE, - data: JSON.parse(event.data), - }); - }, - - restartActivityStream() { - this.closeActivityStream(); - this.startActivityStream(lastActivityStreamOptions); - }, - - startActivityStream(options = {}) { - const {historySnapshot = historySnapshotTypes.FIVE_MINUTE} = options; - const didHistorySnapshotChange = - lastActivityStreamOptions && lastActivityStreamOptions.historySnapshot !== historySnapshot; - - lastActivityStreamOptions = options; - - // When the user requests a new history snapshot during an open session, - // we need to close and re-open the event stream. - if (didHistorySnapshotChange && activityStreamEventSource !== null) { - this.closeActivityStream(); - } - - // If the user requested a new history snapshot, or the event source has not - // alraedy been created, we open the event stream. - if (didHistorySnapshotChange || activityStreamEventSource === null) { - activityStreamEventSource = new EventSource(`${baseURI}api/activity-stream?historySnapshot=${historySnapshot}`); - - activityStreamEventSource.addEventListener( - serverEventTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE, - this.handleClientConnectivityStatusChange, - ); - - activityStreamEventSource.addEventListener(serverEventTypes.DISK_USAGE_CHANGE, this.handleDiskUsageChange); - - activityStreamEventSource.addEventListener( - serverEventTypes.NOTIFICATION_COUNT_CHANGE, - this.handleNotificationCountChange, - ); - - activityStreamEventSource.addEventListener(serverEventTypes.TAXONOMY_DIFF_CHANGE, this.handleTaxonomyDiffChange); - - activityStreamEventSource.addEventListener(serverEventTypes.TAXONOMY_FULL_UPDATE, this.handleTaxonomyFullUpdate); - - activityStreamEventSource.addEventListener( - serverEventTypes.TORRENT_LIST_DIFF_CHANGE, - this.handleTorrentListDiffChange, - ); - - activityStreamEventSource.addEventListener( - serverEventTypes.TORRENT_LIST_FULL_UPDATE, - this.handleTorrentListFullUpdate, - ); - - activityStreamEventSource.addEventListener( - serverEventTypes.TRANSFER_SUMMARY_DIFF_CHANGE, - this.handleTransferSummaryDiffChange, - ); - - activityStreamEventSource.addEventListener( - serverEventTypes.TRANSFER_SUMMARY_FULL_UPDATE, - this.handleTransferSummaryFullUpdate, - ); - - activityStreamEventSource.addEventListener( - serverEventTypes.TRANSFER_HISTORY_FULL_UPDATE, - this.handleTransferHistoryFullUpdate, - ); - } - }, -}; - -const handleProlongedInactivity = () => { - FloodActions.closeActivityStream(); -}; - -const handleWindowVisibilityChange = () => { - if (global.document.hidden) { - // After 30 seconds of inactivity, we stop the event stream. - visibilityChangeTimeout = global.setTimeout(handleProlongedInactivity, 1000 * 30); - } else { - global.clearTimeout(visibilityChangeTimeout); - - if (activityStreamEventSource == null) { - FloodActions.startActivityStream(lastActivityStreamOptions); - } - } -}; - -global.document.addEventListener('visibilitychange', handleWindowVisibilityChange); - -export default FloodActions; diff --git a/client/src/javascript/actions/FloodActions.ts b/client/src/javascript/actions/FloodActions.ts new file mode 100644 index 000000000..dc2870f0b --- /dev/null +++ b/client/src/javascript/actions/FloodActions.ts @@ -0,0 +1,186 @@ +import axios from 'axios'; + +import type {HistorySnapshot} from '@shared/constants/historySnapshotTypes'; +import type {NotificationFetchOptions} from '@shared/types/Notification'; +import type {ServerEvents} from '@shared/types/ServerEvents'; + +import ClientStatusStore from '../stores/ClientStatusStore'; +import ConfigStore from '../stores/ConfigStore'; +import DiskUsageStore from '../stores/DiskUsageStore'; +import NotificationStore from '../stores/NotificationStore'; +import TorrentFilterStore from '../stores/TorrentFilterStore'; +import TorrentStore from '../stores/TorrentStore'; +import TransferDataStore from '../stores/TransferDataStore'; +import UIStore from '../stores/UIStore'; + +interface ActivityStreamOptions { + historySnapshot: HistorySnapshot; +} + +const {baseURI} = ConfigStore; + +let activityStreamEventSource: EventSource | null = null; +let lastActivityStreamOptions: ActivityStreamOptions; +let visibilityChangeTimeout: NodeJS.Timeout; + +// TODO: Use standard Event interfaces +const ServerEventHandlers: Record void> = { + CLIENT_CONNECTIVITY_STATUS_CHANGE: (event: unknown) => { + ClientStatusStore.handleConnectivityStatusChange(JSON.parse((event as {data: string}).data)); + }, + + DISK_USAGE_CHANGE: (event: unknown) => { + DiskUsageStore.setDiskUsage(JSON.parse((event as {data: string}).data)); + }, + + NOTIFICATION_COUNT_CHANGE: (event: unknown) => { + NotificationStore.handleNotificationCountChange(JSON.parse((event as {data: string}).data)); + UIStore.satisfyDependency('notifications'); + }, + + TORRENT_LIST_DIFF_CHANGE: (event: unknown) => { + TorrentStore.handleTorrentListDiffChange(JSON.parse((event as {data: string}).data)); + }, + + TORRENT_LIST_FULL_UPDATE: (event: unknown) => { + TorrentStore.handleTorrentListFullUpdate(JSON.parse((event as {data: string}).data)); + UIStore.satisfyDependency('torrent-list'); + }, + + TAXONOMY_DIFF_CHANGE: (event: unknown) => { + TorrentFilterStore.handleTorrentTaxonomyDiffChange(JSON.parse((event as {data: string}).data)); + }, + + TAXONOMY_FULL_UPDATE: (event: unknown) => { + TorrentFilterStore.handleTorrentTaxonomyFullUpdate(JSON.parse((event as {data: string}).data)); + UIStore.satisfyDependency('torrent-taxonomy'); + }, + + TRANSFER_SUMMARY_DIFF_CHANGE: (event: unknown) => { + TransferDataStore.handleTransferSummaryDiffChange(JSON.parse((event as {data: string}).data)); + }, + + TRANSFER_SUMMARY_FULL_UPDATE: (event: unknown) => { + TransferDataStore.handleTransferSummaryFullUpdate(JSON.parse((event as {data: string}).data)); + UIStore.satisfyDependency('transfer-data'); + }, + + TRANSFER_HISTORY_FULL_UPDATE: (event: unknown) => { + TransferDataStore.handleFetchTransferHistorySuccess(JSON.parse((event as {data: string}).data)); + UIStore.satisfyDependency('transfer-history'); + }, +} as const; + +const FloodActions = { + clearNotifications: () => { + NotificationStore.clearAll(); + return axios + .delete(`${baseURI}api/notifications`) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + // do nothing. + }, + ); + }, + + closeActivityStream() { + if (activityStreamEventSource == null) { + return; + } + + activityStreamEventSource.close(); + + Object.entries(ServerEventHandlers).forEach(([event, handler]) => { + if (activityStreamEventSource != null) { + activityStreamEventSource.removeEventListener(event, handler); + } + }); + + activityStreamEventSource = null; + }, + + fetchDirectoryList: (options = {}) => + axios + .get(`${baseURI}api/directory-list`, { + params: options, + }) + .then((json) => json.data) + .then((response) => ({ + ...options, + ...response, + })), + + fetchNotifications: (options: NotificationFetchOptions) => + axios + .get(`${baseURI}api/notifications`, { + params: { + limit: options.limit, + start: options.start, + }, + }) + .then((json) => json.data) + .then( + (data) => { + NotificationStore.handleNotificationsFetchSuccess(data); + }, + () => { + // do nothing. + }, + ), + + restartActivityStream() { + this.closeActivityStream(); + this.startActivityStream(lastActivityStreamOptions); + }, + + startActivityStream(options: ActivityStreamOptions = {historySnapshot: 'FIVE_MINUTE'}) { + const {historySnapshot} = options; + const didHistorySnapshotChange = + lastActivityStreamOptions && lastActivityStreamOptions.historySnapshot !== historySnapshot; + + lastActivityStreamOptions = options; + + // When the user requests a new history snapshot during an open session, + // we need to close and re-open the event stream. + if (didHistorySnapshotChange && activityStreamEventSource != null) { + this.closeActivityStream(); + } + + // If the user requested a new history snapshot, or the event source has not + // alraedy been created, we open the event stream. + if (didHistorySnapshotChange || activityStreamEventSource == null) { + activityStreamEventSource = new EventSource(`${baseURI}api/activity-stream?historySnapshot=${historySnapshot}`); + + Object.entries(ServerEventHandlers).forEach(([event, handler]) => { + if (activityStreamEventSource != null) { + activityStreamEventSource.addEventListener(event, handler); + } + }); + } + }, +} as const; + +const handleProlongedInactivity = () => { + FloodActions.closeActivityStream(); +}; + +const handleWindowVisibilityChange = () => { + if (global.document.hidden) { + // After 30 seconds of inactivity, we stop the event stream. + visibilityChangeTimeout = global.setTimeout(handleProlongedInactivity, 1000 * 30); + } else { + global.clearTimeout(visibilityChangeTimeout); + + if (activityStreamEventSource == null) { + FloodActions.startActivityStream(lastActivityStreamOptions); + } + } +}; + +global.document.addEventListener('visibilitychange', handleWindowVisibilityChange); + +export default FloodActions; diff --git a/client/src/javascript/actions/SettingActions.ts b/client/src/javascript/actions/SettingActions.ts new file mode 100644 index 000000000..20dd8c3e9 --- /dev/null +++ b/client/src/javascript/actions/SettingActions.ts @@ -0,0 +1,64 @@ +import axios from 'axios'; + +import type {FloodSetting, FloodSettings} from '@shared/types/FloodSettings'; +import type {SetFloodSettingsOptions} from '@shared/types/api/index'; + +import AlertStore from '../stores/AlertStore'; +import ConfigStore from '../stores/ConfigStore'; +import SettingStore from '../stores/SettingStore'; + +const {baseURI} = ConfigStore; + +const SettingActions = { + fetchSettings: async (): Promise => + axios + .get(`${baseURI}api/settings`) + .then((json) => json.data) + .then( + (data) => { + SettingStore.handleSettingsFetchSuccess(data); + }, + () => { + // do nothing. + }, + ), + + saveSettings: async (settings: SetFloodSettingsOptions, options?: {alert?: boolean}): Promise => { + if (Object.keys(settings).length > 0) { + SettingStore.saveFloodSettings(settings); + + let err = false; + await axios + .patch(`${baseURI}api/settings`, settings) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + err = true; + }, + ); + + if (options?.alert) { + // TODO: More precise error message. + AlertStore.add( + err + ? { + id: 'general.error.unknown', + type: 'error', + } + : { + id: 'alert.settings.saved', + type: 'success', + }, + ); + } + } + }, + + saveSetting: async (property: T, data: FloodSettings[T]): Promise => + SettingActions.saveSettings({[property]: data}), +} as const; + +export default SettingActions; diff --git a/client/src/javascript/actions/SettingsActions.js b/client/src/javascript/actions/SettingsActions.js deleted file mode 100644 index 2d7c2e39b..000000000 --- a/client/src/javascript/actions/SettingsActions.js +++ /dev/null @@ -1,208 +0,0 @@ -import axios from 'axios'; - -import AppDispatcher from '../dispatcher/AppDispatcher'; -import ActionTypes from '../constants/ActionTypes'; -import ConfigStore from '../stores/ConfigStore'; - -const baseURI = ConfigStore.getBaseURI(); - -const SettingsActions = { - addFeed: feed => - axios - .put(`${baseURI}api/feed-monitor/feeds`, feed) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_FEED_ADD_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_FEED_ADD_ERROR, - error, - }); - }, - ), - - modifyFeed: (id, feed) => - axios - .put(`${baseURI}api/feed-monitor/feeds/${id}`, feed) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_FEED_MODIFY_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_FEED_MODiFY_ERROR, - error, - }); - }, - ), - - addRule: rule => - axios - .put(`${baseURI}api/feed-monitor/rules`, rule) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_RULE_ADD_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_RULE_ADD_ERROR, - error, - }); - }, - ), - - fetchFeedMonitors: query => - axios - .get(`${baseURI}api/feed-monitor`, query) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITORS_FETCH_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITORS_FETCH_ERROR, - error, - }); - }, - ), - - fetchFeeds: query => - axios - .get(`${baseURI}api/feed-monitor/feeds`, query) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_FEEDS_FETCH_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_FEEDS_FETCH_ERROR, - error, - }); - }, - ), - - fetchItems: query => - axios - .get(`${baseURI}api/feed-monitor/items`, query) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_ITEMS_FETCH_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_ITEMS_FETCH_ERROR, - error, - }); - }, - ), - - fetchRules: query => - axios - .get(`${baseURI}api/feed-monitor/rules`, query) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_RULES_FETCH_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_RULES_FETCH_ERROR, - error, - }); - }, - ), - - fetchSettings: property => - axios - .get(`${baseURI}api/settings`, {params: {property}}) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FETCH_REQUEST_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FETCH_REQUEST_ERROR, - error, - }); - }, - ), - - removeFeedMonitor: id => - axios - .delete(`${baseURI}api/feed-monitor/${id}`) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_REMOVE_SUCCESS, - data: { - ...data, - id, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_FEED_MONITOR_REMOVE_ERROR, - error: { - ...error, - id, - }, - }); - }, - ), - - saveSettings: (settings, options = {}) => - axios - .patch(`${baseURI}api/settings`, settings) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_SAVE_REQUEST_SUCCESS, - data, - options, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.SETTINGS_SAVE_REQUEST_ERROR, - error, - }); - }, - ), -}; - -export default SettingsActions; diff --git a/client/src/javascript/actions/TorrentActions.js b/client/src/javascript/actions/TorrentActions.js deleted file mode 100644 index 0a97ae869..000000000 --- a/client/src/javascript/actions/TorrentActions.js +++ /dev/null @@ -1,293 +0,0 @@ -import axios from 'axios'; - -import AppDispatcher from '../dispatcher/AppDispatcher'; -import ActionTypes from '../constants/ActionTypes'; -import ConfigStore from '../stores/ConfigStore'; - -const baseURI = ConfigStore.getBaseURI(); - -const TorrentActions = { - addTorrentsByUrls: options => - axios - .post(`${baseURI}api/client/add`, options) - .then((json = {}) => json.data) - .then( - response => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_ADD_TORRENT_SUCCESS, - data: { - count: options.urls.length, - destination: options.destination, - response, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_ADD_TORRENT_ERROR, - data: { - error, - }, - }); - }, - ), - - addTorrentsByFiles: (formData, destination) => - axios - .post(`${baseURI}api/client/add-files`, formData) - .then((json = {}) => json.data) - .then( - response => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_ADD_TORRENT_SUCCESS, - data: { - count: formData.getAll('torrents').length, - destination, - response, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_ADD_TORRENT_ERROR, - data: { - error, - }, - }); - }, - ), - - deleteTorrents: (hash, deleteData) => - axios - .post(`${baseURI}api/client/torrents/delete`, {hash, deleteData}) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_REMOVE_TORRENT_SUCCESS, - data: { - data, - count: hash.length, - deleteData, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_REMOVE_TORRENT_ERROR, - error: { - error, - count: hash.length, - }, - }); - }, - ), - - checkHash: hash => - axios - .post(`${baseURI}api/client/torrents/check-hash`, {hash}) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_CHECK_HASH_SUCCESS, - data: { - data, - count: hash.length, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_CHECK_HASH_ERROR, - error: { - error, - count: hash.length, - }, - }); - }, - ), - - fetchTorrentDetails: hash => - axios - .post(`${baseURI}api/client/torrent-details`, { - hash, - }) - .then((json = {}) => json.data) - .then( - torrentDetails => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_FETCH_TORRENT_DETAILS_SUCCESS, - data: { - hash, - torrentDetails, - }, - }); - }, - () => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_FETCH_TORRENT_DETAILS_ERROR, - data: { - hash, - }, - }); - }, - ), - - moveTorrents: (hashes, options) => { - const {destination, isBasePath, filenames, sourcePaths, moveFiles} = options; - - return axios - .post(`${baseURI}api/client/torrents/move`, { - hashes, - destination, - isBasePath, - filenames, - sourcePaths, - moveFiles, - }) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_MOVE_TORRENTS_SUCCESS, - data: { - data, - count: hashes.length, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_MOVE_TORRENTS_ERROR, - error, - }); - }, - ); - }, - - startTorrents: hashes => - axios - .post(`${baseURI}api/client/start`, { - hashes, - }) - .then((json = {}) => json.data) - .then( - response => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_START_TORRENT_SUCCESS, - data: { - response, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_START_TORRENT_ERROR, - data: { - error, - }, - }); - }, - ), - - stopTorrents: hashes => - axios - .post(`${baseURI}api/client/stop`, { - hashes, - }) - .then((json = {}) => json.data) - .then( - response => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_STOP_TORRENT_SUCCESS, - data: { - response, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_STOP_TORRENT_ERROR, - data: { - error, - }, - }); - }, - ), - - setPriority: (hash, priority) => - axios - .patch(`${baseURI}api/client/torrents/${hash}/priority`, { - hash, - priority, - }) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SET_TORRENT_PRIORITY_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SET_TORRENT_PRIORITY_ERROR, - error, - }); - }, - ), - - setFilePriority: (hash, fileIndices, priority) => - axios - .patch(`${baseURI}api/client/torrents/${hash}/file-priority`, { - hash, - fileIndices, - priority, - }) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SET_FILE_PRIORITY_SUCCESS, - data: { - ...data, - hash, - fileIndices, - priority, - }, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SET_FILE_PRIORITY_ERROR, - error, - }); - }, - ), - - setTaxonomy: (hashes, tags, options = {}) => - axios - .patch(`${baseURI}api/client/torrents/taxonomy`, { - hashes, - tags, - options, - }) - .then((json = {}) => json.data) - .then( - data => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SET_TAXONOMY_SUCCESS, - data, - }); - }, - error => { - AppDispatcher.dispatchServerAction({ - type: ActionTypes.CLIENT_SET_TAXONOMY_ERROR, - error, - }); - }, - ), -}; - -export default TorrentActions; diff --git a/client/src/javascript/actions/TorrentActions.ts b/client/src/javascript/actions/TorrentActions.ts new file mode 100644 index 000000000..6e7d9e1cb --- /dev/null +++ b/client/src/javascript/actions/TorrentActions.ts @@ -0,0 +1,249 @@ +import axios, {CancelToken} from 'axios'; +import download from 'js-file-download'; + +import type { + AddTorrentByFileOptions, + AddTorrentByURLOptions, + SetTorrentsTagsOptions, +} from '@shared/schema/api/torrents'; +import type { + CheckTorrentsOptions, + CreateTorrentOptions, + DeleteTorrentsOptions, + MoveTorrentsOptions, + SetTorrentContentsPropertiesOptions, + SetTorrentsPriorityOptions, + SetTorrentsTrackersOptions, + StartTorrentsOptions, + StopTorrentsOptions, +} from '@shared/types/api/torrents'; +import type {TorrentContent} from '@shared/types/TorrentContent'; +import type {TorrentPeer} from '@shared/types/TorrentPeer'; +import type {TorrentTracker} from '@shared/types/TorrentTracker'; +import type {TorrentProperties} from '@shared/types/Torrent'; + +import AlertStore from '../stores/AlertStore'; +import ConfigStore from '../stores/ConfigStore'; +import UIStore from '../stores/UIStore'; + +const {baseURI} = ConfigStore; + +const emitTorrentAddedAlert = (count: number) => { + AlertStore.add({ + id: 'alert.torrent.add', + type: 'success', + count, + }); +}; + +const TorrentActions = { + addTorrentsByUrls: (options: AddTorrentByURLOptions) => + axios + .post(`${baseURI}api/torrents/add-urls`, options) + .then((json) => json.data) + .then( + () => { + emitTorrentAddedAlert(options.urls.length); + }, + () => { + // do nothing. + }, + ), + + addTorrentsByFiles: (options: AddTorrentByFileOptions) => + axios + .post(`${baseURI}api/torrents/add-files`, options) + .then((json) => json.data) + .then( + () => { + emitTorrentAddedAlert(options.files.length); + }, + () => { + // do nothing. + }, + ), + + createTorrent: (options: CreateTorrentOptions) => + axios.post(`${baseURI}api/torrents/create`, options, {responseType: 'blob'}).then( + (response) => { + download(response.data, (options.name || `${Date.now()}`).concat('.torrent')); + emitTorrentAddedAlert(1); + }, + () => { + // do nothing. + }, + ), + + deleteTorrents: (options: DeleteTorrentsOptions) => + axios + .post(`${baseURI}api/torrents/delete`, options) + .then((json) => json.data) + .then( + () => { + AlertStore.add({ + id: 'alert.torrent.remove', + type: 'success', + count: options.hashes.length, + }); + }, + () => { + AlertStore.add({ + id: 'alert.torrent.remove.failed', + type: 'error', + count: options.hashes.length, + }); + }, + ), + + checkHash: (options: CheckTorrentsOptions) => + axios + .post(`${baseURI}api/torrents/check-hash`, options) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + // do nothing. + }, + ), + + fetchMediainfo: (hash: TorrentProperties['hash'], cancelToken?: CancelToken): Promise<{output: string}> => + axios.get(`${baseURI}api/torrents/${hash}/mediainfo`, {cancelToken}).then<{output: string}>((json) => json.data), + + fetchTorrentContents: (hash: TorrentProperties['hash']): Promise | null> => + axios + .get(`${baseURI}api/torrents/${hash}/contents`) + .then>((json) => json.data) + .then( + (contents) => contents, + () => null, + ), + + fetchTorrentPeers: (hash: TorrentProperties['hash']): Promise | null> => + axios + .get(`${baseURI}api/torrents/${hash}/peers`) + .then>((json) => json.data) + .then( + (peers) => peers, + () => null, + ), + + fetchTorrentTrackers: (hash: TorrentProperties['hash']): Promise | null> => + axios + .get(`${baseURI}api/torrents/${hash}/trackers`) + .then>((json) => json.data) + .then( + (trackers) => trackers, + () => null, + ), + + moveTorrents: (options: MoveTorrentsOptions) => + axios + .post(`${baseURI}api/torrents/move`, options) + .then((json) => json.data) + .then( + () => { + AlertStore.add({ + id: 'alert.torrent.move', + type: 'success', + count: options.hashes.length, + }); + }, + () => { + AlertStore.add({ + id: 'alert.torrent.move.failed', + type: 'error', + count: options.hashes.length, + }); + }, + ), + + startTorrents: async (options: StartTorrentsOptions): Promise => { + if (options.hashes.length > 0) { + return axios + .post(`${baseURI}api/torrents/start`, options) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + // do nothing. + }, + ); + } + return undefined; + }, + + stopTorrents: async (options: StopTorrentsOptions): Promise => { + if (options.hashes.length > 0) { + return axios + .post(`${baseURI}api/torrents/stop`, options) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + // do nothing. + }, + ); + } + return undefined; + }, + + setPriority: (options: SetTorrentsPriorityOptions) => + axios + .patch(`${baseURI}api/torrents/priority`, options) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + // do nothing. + }, + ), + + setFilePriority: (hash: TorrentProperties['hash'], options: SetTorrentContentsPropertiesOptions) => + axios + .patch(`${baseURI}api/torrents/${hash}/contents`, options) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + // do nothing. + }, + ), + + setTags: (options: SetTorrentsTagsOptions) => + axios + .patch(`${baseURI}api/torrents/tags`, options) + .then((json) => json.data) + .then( + () => { + UIStore.handleSetTaxonomySuccess(); + }, + () => { + // do nothing. + }, + ), + + setTrackers: (options: SetTorrentsTrackersOptions) => + axios + .patch(`${baseURI}api/torrents/trackers`, options) + .then((json) => json.data) + .then( + () => { + // do nothing. + }, + () => { + // do nothing. + }, + ), +}; + +export default TorrentActions; diff --git a/client/src/javascript/actions/UIActions.js b/client/src/javascript/actions/UIActions.js deleted file mode 100644 index 7b4355fa3..000000000 --- a/client/src/javascript/actions/UIActions.js +++ /dev/null @@ -1,96 +0,0 @@ -import _ from 'lodash'; - -import AppDispatcher from '../dispatcher/AppDispatcher'; -import ActionTypes from '../constants/ActionTypes'; - -const UIActions = { - displayContextMenu: data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_DISPLAY_CONTEXT_MENU, - data, - }); - }, - - displayDropdownMenu: data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_DISPLAY_DROPDOWN_MENU, - data, - }); - }, - - displayModal: data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_DISPLAY_MODAL, - data, - }); - }, - - dismissContextMenu: contextMenuID => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_DISMISS_CONTEXT_MENU, - data: contextMenuID, - }); - }, - - dismissModal: () => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_DISPLAY_MODAL, - data: null, - }); - }, - - handleDetailsClick: data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_CLICK_TORRENT_DETAILS, - data, - }); - }, - - handleTorrentClick: data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_CLICK_TORRENT, - data, - }); - }, - - setTorrentStatusFilter: data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_SET_TORRENT_STATUS_FILTER, - data, - }); - }, - - setTorrentTagFilter: data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_SET_TORRENT_TAG_FILTER, - data, - }); - }, - - setTorrentTrackerFilter: data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_SET_TORRENT_TRACKER_FILTER, - data, - }); - }, - - setTorrentsSearchFilter: _.debounce( - data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_SET_TORRENT_SEARCH_FILTER, - data, - }); - }, - 250, - {trailing: true}, - ), - - setTorrentsSort: data => { - AppDispatcher.dispatchUIAction({ - type: ActionTypes.UI_SET_TORRENT_SORT, - data, - }); - }, -}; - -export default UIActions; diff --git a/client/src/javascript/actions/UIActions.ts b/client/src/javascript/actions/UIActions.ts new file mode 100644 index 000000000..5a5acc448 --- /dev/null +++ b/client/src/javascript/actions/UIActions.ts @@ -0,0 +1,58 @@ +import debounce from 'lodash/debounce'; +import * as React from 'react'; + +import type {TorrentStatus} from '@shared/constants/torrentStatusMap'; + +import TorrentFilterStore from '../stores/TorrentFilterStore'; +import TorrentStore from '../stores/TorrentStore'; +import UIStore from '../stores/UIStore'; + +import type {ActiveContextMenu, Modal} from '../stores/UIStore'; + +const UIActions = { + displayContextMenu: (contextMenu: ActiveContextMenu) => { + UIStore.setActiveContextMenu(contextMenu); + }, + + displayDropdownMenu: (id: string | null) => { + UIStore.setActiveDropdownMenu(id); + }, + + displayModal: (modal: Modal) => { + UIStore.setActiveModal(modal); + }, + + dismissContextMenu: (id: string) => { + UIStore.dismissContextMenu(id); + }, + + dismissModal: () => { + UIStore.dismissModal(); + }, + + handleTorrentClick: (data: {event: React.MouseEvent | React.TouchEvent; hash: string}) => { + TorrentStore.setSelectedTorrents(data); + }, + + setTorrentStatusFilter: (status: TorrentStatus) => { + TorrentFilterStore.setStatusFilter(status); + }, + + setTorrentTagFilter: (tag: string) => { + TorrentFilterStore.setTagFilter(tag); + }, + + setTorrentTrackerFilter: (tracker: string) => { + TorrentFilterStore.setTrackerFilter(tracker); + }, + + setTorrentsSearchFilter: debounce( + (search: string) => { + TorrentFilterStore.setSearchFilter(search); + }, + 250, + {trailing: true}, + ), +} as const; + +export default UIActions; diff --git a/client/src/javascript/app.tsx b/client/src/javascript/app.tsx index a69b56a4d..4bd8be800 100644 --- a/client/src/javascript/app.tsx +++ b/client/src/javascript/app.tsx @@ -1,114 +1,96 @@ +import {observer} from 'mobx-react'; +import {QueryParamProvider} from 'use-query-params'; +import {FC, lazy, Suspense, useEffect} from 'react'; import {Router} from 'react-router-dom'; -import {FormattedMessage, IntlProvider} from 'react-intl'; -import {Route} from 'react-router'; -import React from 'react'; +import {Route, Switch} from 'react-router'; import ReactDOM from 'react-dom'; +import {useMedia} from 'react-use'; -import * as i18n from './i18n/languages'; -import connectStores, {EventListenerDescriptor} from './util/connectStores'; +import AsyncIntlProvider from './i18n/languages'; import AppWrapper from './components/AppWrapper'; import AuthActions from './actions/AuthActions'; -import EventTypes from './constants/EventTypes'; -import FloodActions from './actions/FloodActions'; import history from './util/history'; -import Login from './components/views/Login'; -import Register from './components/views/Register'; -import SettingsStore from './stores/SettingsStore'; -import TorrentClientOverview from './components/views/TorrentClientOverview'; +import LoadingOverlay from './components/general/LoadingOverlay'; +import ConfigStore from './stores/ConfigStore'; +import SettingStore from './stores/SettingStore'; import UIStore from './stores/UIStore'; import '../sass/style.scss'; -const initialize = (): void => { - UIStore.registerDependency({ - id: 'notifications', - message: , - }); +const Login = lazy(() => import(/* webpackPrefetch: true */ './components/views/Login')); +const Register = lazy(() => import(/* webpackPrefetch: true */ './components/views/Register')); +const TorrentClientOverview = lazy(() => import(/* webpackPreload: true */ './components/views/TorrentClientOverview')); - UIStore.registerDependency({ - id: 'torrent-taxonomy', - message: , - }); - - UIStore.registerDependency([ - { - id: 'transfer-data', - message: ( - - ), - }, - { - id: 'transfer-history', - message: , - }, - ]); - - UIStore.registerDependency({ - id: 'torrent-list', - message: , - }); - - AuthActions.verify().then( - ({initialUser}): void => { - if (initialUser) { - history.replace('register'); - } else { - history.replace('overview'); - } - }, - (): void => { - history.replace('login'); - }, - ); - - FloodActions.startActivityStream(); -}; - -const appRoutes = ( - - - - - - - -); +const FloodApp: FC = observer(() => { + useEffect(() => { + UIStore.registerDependency([ + { + id: 'notifications', + message: {id: 'dependency.loading.notifications'}, + }, + ]); -interface InjectedFloodAppProps { - locale: keyof typeof i18n; -} + UIStore.registerDependency([ + { + id: 'torrent-taxonomy', + message: {id: 'dependency.loading.torrent.taxonomy'}, + }, + ]); -class FloodApp extends React.Component { - public componentDidMount(): void { - initialize(); - } + UIStore.registerDependency([ + { + id: 'transfer-data', + message: {id: 'dependency.loading.transfer.rate.details'}, + }, + { + id: 'transfer-history', + message: {id: 'dependency.loading.transfer.history'}, + }, + ]); - public render(): React.ReactNode { - const {locale} = this.props; + UIStore.registerDependency([ + { + id: 'torrent-list', + message: {id: 'dependency.loading.torrent.list'}, + }, + ]); - return ( - - {appRoutes} - + AuthActions.verify().then( + ({initialUser}: {initialUser?: boolean}): void => { + if (initialUser) { + history.replace('register'); + } else { + history.replace('overview'); + } + }, + (): void => { + history.replace('login'); + }, ); - } -} + }, []); -const ConnectedFloodApp = connectStores( - FloodApp, - (): EventListenerDescriptor[] => { - return [ - { - store: SettingsStore, - event: EventTypes.SETTINGS_CHANGE, - getValue: (): InjectedFloodAppProps => { - return { - locale: SettingsStore.getFloodSettings('language'), - }; - }, - }, - ]; - }, -); + const isDarkTheme = useMedia('(prefers-color-scheme: dark)'); + useEffect(() => { + ConfigStore.systemPreferDark = isDarkTheme; + }, [isDarkTheme]); + + return ( + }> + + + + + + + + + + + + + + + ); +}); -ReactDOM.render(, document.getElementById('app')); +ReactDOM.render(, document.getElementById('app')); diff --git a/client/src/javascript/components/AppWrapper.js b/client/src/javascript/components/AppWrapper.js deleted file mode 100644 index 19ce1bd1a..000000000 --- a/client/src/javascript/components/AppWrapper.js +++ /dev/null @@ -1,148 +0,0 @@ -import classnames from 'classnames'; -import {CSSTransition, TransitionGroup} from 'react-transition-group'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import AuthStore from '../stores/AuthStore'; -import Checkmark from './icons/Checkmark'; -import ClientConnectionInterruption from './general/ClientConnectionInterruption'; -import ClientStatusStore from '../stores/ClientStatusStore'; -import connectStores from '../util/connectStores'; -import EventTypes from '../constants/EventTypes'; -import LoadingIndicator from './general/LoadingIndicator'; -import UIStore from '../stores/UIStore'; -import WindowTitle from './general/WindowTitle'; - -const ICONS = { - satisfied: , -}; - -class AuthEnforcer extends React.Component { - static propTypes = { - children: PropTypes.node, - }; - - isLoading() { - const {dependencies, dependenciesLoaded, isAuthenticated, isAuthenticating} = this.props; - // If the auth status is undetermined, show the loading indicator. - if (!isAuthenticating) return true; - // Allow the UI to load if the user is not authenticated. - if (!isAuthenticated) return false; - // Iterate over current dependencies looking for unsatisified dependencies. - const isDependencyActive = Object.keys(dependencies).some(dependencyKey => !dependencies[dependencyKey].satisfied); - // If any dependency is unsatisfied, show the loading indicator. - if (isDependencyActive) return true; - // Dismiss the loading indicator if the UI store thinks all dependencies - // are loaded. - return !dependenciesLoaded; - } - - renderOverlay() { - const {isAuthenticated, isClientConnected} = this.props; - let content; - - if (this.isLoading()) { - content = ( -
- - {this.renderDependencyList()} -
- ); - } - - if (isAuthenticated && !isClientConnected) { - content = ( -
-
- -
-
- ); - } - - return content != null ? ( - - {content} - - ) : null; - } - - renderDependencyList() { - const {dependencies} = this.props; - const listItems = Object.keys(dependencies).map(id => { - const {message, satisfied} = dependencies[id]; - const statusIcon = ICONS.satisfied; - const classes = classnames('dependency-list__dependency', { - 'dependency-list__dependency--satisfied': satisfied, - }); - - return ( -
  • - {statusIcon} - {message} -
  • - ); - }); - - return
      {listItems}
    ; - } - - render() { - return ( -
    - - {this.renderOverlay()} - {this.props.children} -
    - ); - } -} - -const ConnectedAuthEnforcer = connectStores(AuthEnforcer, () => { - return [ - { - store: AuthStore, - event: [ - EventTypes.AUTH_LOGIN_SUCCESS, - EventTypes.AUTH_REGISTER_SUCCESS, - EventTypes.AUTH_VERIFY_SUCCESS, - EventTypes.AUTH_VERIFY_ERROR, - ], - getValue: ({store}) => { - return { - isAuthenticating: store.getIsAuthenticating(), - isAuthenticated: store.getIsAuthenticated(), - }; - }, - }, - { - store: UIStore, - event: EventTypes.UI_DEPENDENCIES_CHANGE, - getValue: ({store}) => { - return { - dependencies: store.getDependencies(), - }; - }, - }, - { - store: UIStore, - event: EventTypes.UI_DEPENDENCIES_LOADED, - getValue: ({store}) => { - return { - dependenciesLoaded: store.haveUIDependenciesResolved, - }; - }, - }, - { - store: ClientStatusStore, - event: EventTypes.CLIENT_CONNECTION_STATUS_CHANGE, - getValue: ({store}) => { - return { - isClientConnected: store.getIsConnected(), - }; - }, - }, - ]; -}); - -export default ConnectedAuthEnforcer; diff --git a/client/src/javascript/components/AppWrapper.tsx b/client/src/javascript/components/AppWrapper.tsx new file mode 100644 index 000000000..4b2bf0376 --- /dev/null +++ b/client/src/javascript/components/AppWrapper.tsx @@ -0,0 +1,70 @@ +import classnames from 'classnames'; +import {CSSTransition, TransitionGroup} from 'react-transition-group'; +import {FC, ReactNode} from 'react'; +import {observer} from 'mobx-react'; +import {useQueryParams, StringParam} from 'use-query-params'; + +import AuthStore from '../stores/AuthStore'; +import ConfigStore from '../stores/ConfigStore'; +import ClientConnectionInterruption from './general/ClientConnectionInterruption'; +import ClientStatusStore from '../stores/ClientStatusStore'; +import UIStore from '../stores/UIStore'; +import WindowTitle from './general/WindowTitle'; +import LoadingOverlay from './general/LoadingOverlay'; + +interface AppWrapperProps { + children: ReactNode; + className?: string; +} + +const AppWrapper: FC = observer((props: AppWrapperProps) => { + const {children, className} = props; + + const [query] = useQueryParams({action: StringParam, url: StringParam}); + + if (query.action) { + if (query.action === 'add-urls') { + if (query.url) { + UIStore.setActiveModal({ + id: 'add-torrents', + initialURLs: [{id: 0, value: query.url}], + }); + } + } + } + + let overlay: ReactNode = null; + if (!AuthStore.isAuthenticating || (AuthStore.isAuthenticated && !UIStore.haveUIDependenciesResolved)) { + overlay = ; + } + + if (AuthStore.isAuthenticated && !ClientStatusStore.isConnected && ConfigStore.authMethod !== 'none') { + overlay = ( +
    +
    + +
    +
    + ); + } + + return ( +
    + + + {overlay != null ? ( + + {overlay} + + ) : null} + + {children} +
    + ); +}); + +AppWrapper.defaultProps = { + className: undefined, +}; + +export default AppWrapper; diff --git a/client/src/javascript/components/alerts/Alert.js b/client/src/javascript/components/alerts/Alert.js deleted file mode 100644 index 9ffe57a95..000000000 --- a/client/src/javascript/components/alerts/Alert.js +++ /dev/null @@ -1,48 +0,0 @@ -import {FormattedMessage} from 'react-intl'; -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import Alerts from '../../constants/Alerts'; -import CircleCheckmarkIcon from '../icons/CircleCheckmarkIcon'; -import CircleExclamationIcon from '../icons/CircleExclamationIcon'; - -export default class Alert extends React.Component { - static propTypes = { - count: PropTypes.number, - id: PropTypes.string, - }; - - static defaultProps = { - count: 0, - type: 'success', - }; - - render() { - let icon = ; - const alertClasses = classnames('alert', { - 'is-success': this.props.type === 'success', - 'is-error': this.props.type === 'error', - }); - - if (this.props.type === 'error') { - icon = ; - } - - return ( -
  • - {icon} - - {this.props.count}, - }} - /> - -
  • - ); - } -} diff --git a/client/src/javascript/components/alerts/Alert.tsx b/client/src/javascript/components/alerts/Alert.tsx new file mode 100644 index 000000000..0cba129a0 --- /dev/null +++ b/client/src/javascript/components/alerts/Alert.tsx @@ -0,0 +1,48 @@ +import {FC} from 'react'; +import {FormattedMessage} from 'react-intl'; +import classnames from 'classnames'; +import {observer} from 'mobx-react'; + +import AlertStore from '../../stores/AlertStore'; +import CircleCheckmarkIcon from '../icons/CircleCheckmarkIcon'; +import CircleExclamationIcon from '../icons/CircleExclamationIcon'; + +interface AlertProps { + id: string; +} + +const Alert: FC = observer((props: AlertProps) => { + const {id} = props; + const {count, type} = AlertStore.alerts[id] || {}; + + if (id == null || count == null || type == null) { + return null; + } + + const alertClasses = classnames('alert', { + 'is-success': type === 'success', + 'is-error': type === 'error', + }); + + let icon = ; + if (type === 'error') { + icon = ; + } + + return ( +
  • + {icon} + + {count}, + }} + /> + +
  • + ); +}); + +export default Alert; diff --git a/client/src/javascript/components/alerts/Alerts.js b/client/src/javascript/components/alerts/Alerts.js deleted file mode 100644 index c2d3877e6..000000000 --- a/client/src/javascript/components/alerts/Alerts.js +++ /dev/null @@ -1,47 +0,0 @@ -import {CSSTransition, TransitionGroup} from 'react-transition-group'; -import React from 'react'; - -import Alert from './Alert'; -import AlertStore from '../../stores/AlertStore'; -import connectStores from '../../util/connectStores'; -import EventTypes from '../../constants/EventTypes'; - -class Alerts extends React.Component { - renderAlerts() { - const {alerts} = this.props; - - if (alerts.length > 0) { - return ( - -
      - {this.props.alerts.map(alert => ( - - ))} -
    -
    - ); - } - - return null; - } - - render() { - return {this.renderAlerts()}; - } -} - -const ConnectedAlerts = connectStores(Alerts, () => { - return [ - { - store: AlertStore, - event: EventTypes.ALERTS_CHANGE, - getValue: ({store}) => { - return { - alerts: store.getAlerts(), - }; - }, - }, - ]; -}); - -export default ConnectedAlerts; diff --git a/client/src/javascript/components/alerts/Alerts.tsx b/client/src/javascript/components/alerts/Alerts.tsx new file mode 100644 index 000000000..e8c4f105b --- /dev/null +++ b/client/src/javascript/components/alerts/Alerts.tsx @@ -0,0 +1,26 @@ +import {CSSTransition, TransitionGroup} from 'react-transition-group'; +import {FC} from 'react'; +import {observer} from 'mobx-react'; + +import Alert from './Alert'; +import AlertStore from '../../stores/AlertStore'; + +const Alerts: FC = observer(() => { + const {sortedAlerts} = AlertStore; + + return ( + + {sortedAlerts != null && sortedAlerts.length > 0 ? ( + +
      + {sortedAlerts.map((alert) => ( + + ))} +
    +
    + ) : null} +
    + ); +}); + +export default Alerts; diff --git a/client/src/javascript/components/auth/AuthForm.js b/client/src/javascript/components/auth/AuthForm.js deleted file mode 100644 index ce8ffc047..000000000 --- a/client/src/javascript/components/auth/AuthForm.js +++ /dev/null @@ -1,149 +0,0 @@ -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import {Button, Form, FormError, FormRow, Panel, PanelContent, PanelHeader, PanelFooter, Textbox} from 'flood-ui-kit'; -import AuthActions from '../../actions/AuthActions'; -import AuthStore from '../../stores/AuthStore'; -import connectStores from '../../util/connectStores'; -import history from '../../util/history'; -import EventTypes from '../../constants/EventTypes'; -import RtorrentConnectionTypeSelection from '../general/RtorrentConnectionTypeSelection'; - -class AuthForm extends React.Component { - state = { - isSubmitting: false, - }; - - getHeaderText() { - if (this.props.mode === 'login') { - return this.props.intl.formatMessage({ - id: 'auth.login', - defaultMessage: 'Login', - }); - } - - return this.props.intl.formatMessage({ - id: 'auth.create.an.account', - defaultMessage: 'Create an account', - }); - } - - getIntroText() { - if (this.props.mode === 'login') { - return this.props.intl.formatMessage({ - id: 'auth.login.intro', - defaultMessage: 'Log in to your account.', - }); - } - - return this.props.intl.formatMessage({ - id: 'auth.create.an.account.intro', - defaultMessage: 'Welcome to Flood! Create a username and strong password.', - }); - } - - handleFormSubmit = submission => { - submission.event.preventDefault(); - - this.setState({isSubmitting: true}); - - if (this.props.mode === 'login') { - AuthActions.authenticate({ - username: submission.formData.username, - password: submission.formData.password, - }) - .then(() => { - this.setState({isSubmitting: false}, () => history.replace('overview')); - }) - .catch(() => { - this.setState({isSubmitting: false}, () => history.replace('login')); - }); - } else { - AuthActions.register({ - username: submission.formData.username, - password: submission.formData.password, - host: submission.formData.rtorrentHost, - port: submission.formData.rtorrentPort, - socketPath: submission.formData.rtorrentSocketPath, - isAdmin: true, - }).then(() => { - this.setState({isSubmitting: false}, () => history.replace('overview')); - }); - } - }; - - render() { - const {isSubmitting} = this.state; - const {error, intl, mode} = this.props; - const isLoginMode = mode === 'login'; - - return ( -
    - -
    { - this.formRef = ref; - }}> - -

    {this.getHeaderText()}

    -
    - -

    {this.getIntroText()}

    - {error != null ? ( - - {error} - - ) : null} - - - - - - -
    - {isLoginMode ? null : ( - - - - )} - - - - - - -
    -
    -
    - ); - } -} - -const ConnectedAuthForm = connectStores(injectIntl(AuthForm), () => { - return [ - { - store: AuthStore, - event: [EventTypes.AUTH_LOGIN_ERROR, EventTypes.AUTH_REGISTER_ERROR], - getValue: ({payload}) => { - return { - error: payload, - }; - }, - }, - ]; -}); - -export default ConnectedAuthForm; diff --git a/client/src/javascript/components/auth/AuthForm.tsx b/client/src/javascript/components/auth/AuthForm.tsx new file mode 100644 index 000000000..c849296ea --- /dev/null +++ b/client/src/javascript/components/auth/AuthForm.tsx @@ -0,0 +1,218 @@ +import {injectIntl, WrappedComponentProps} from 'react-intl'; +import * as React from 'react'; + +import {AccessLevel} from '@shared/schema/constants/Auth'; + +import type {Credentials} from '@shared/schema/Auth'; + +import {Button, Form, FormError, FormRow, Panel, PanelContent, PanelHeader, PanelFooter, Textbox} from '../../ui'; +import AuthActions from '../../actions/AuthActions'; +import ClientConnectionSettingsForm from '../general/connection-settings/ClientConnectionSettingsForm'; +import history from '../../util/history'; + +import type {ClientConnectionSettingsFormType} from '../general/connection-settings/ClientConnectionSettingsForm'; + +type LoginFormData = Pick; +type RegisterFormData = Pick; + +interface AuthFormProps extends WrappedComponentProps { + mode: 'login' | 'register'; +} + +interface AuthFormStates { + isSubmitting: boolean; + errorMessage?: string; +} + +class AuthForm extends React.Component { + formRef?: Form | null; + settingsFormRef: React.RefObject = React.createRef(); + + constructor(props: AuthFormProps) { + super(props); + this.state = { + isSubmitting: false, + }; + } + + getHeaderText() { + const {mode, intl} = this.props; + + if (mode === 'login') { + return intl.formatMessage({ + id: 'auth.login', + }); + } + + return intl.formatMessage({ + id: 'auth.create.an.account', + }); + } + + getIntroText() { + const {mode, intl} = this.props; + + if (mode === 'login') { + return intl.formatMessage({ + id: 'auth.login.intro', + }); + } + + return intl.formatMessage({ + id: 'auth.create.an.account.intro', + }); + } + + handleFormSubmit = (submission: { + event: Event | React.FormEvent; + formData: Record; + }) => { + submission.event.preventDefault(); + + this.setState({isSubmitting: true}); + + const {intl, mode} = this.props; + + const formData = submission.formData as Partial | Partial; + + if (formData.username == null || formData.username === '') { + this.setState({ + isSubmitting: false, + errorMessage: intl.formatMessage({id: 'auth.error.username.empty'}), + }); + return; + } + + if (formData.password == null || formData.password === '') { + this.setState({ + isSubmitting: false, + errorMessage: intl.formatMessage({id: 'auth.error.password.empty'}), + }); + return; + } + + if (mode === 'login') { + const credentials = formData as LoginFormData; + + AuthActions.authenticate({ + username: credentials.username, + password: credentials.password, + }) + .then(() => { + this.setState({isSubmitting: false}, () => history.replace('overview')); + }) + .catch((error: Error) => { + this.setState({isSubmitting: false, errorMessage: error.message}, () => history.replace('login')); + }); + } else { + const config = formData as RegisterFormData; + + if (this.settingsFormRef.current == null) { + this.setState({ + isSubmitting: false, + errorMessage: intl.formatMessage({ + id: 'connection.settings.error.empty', + }), + }); + return; + } + + const connectionSettings = this.settingsFormRef.current.getConnectionSettings(); + if (connectionSettings == null) { + this.setState({ + isSubmitting: false, + errorMessage: intl.formatMessage({ + id: 'connection.settings.error.empty', + }), + }); + return; + } + + AuthActions.register({ + username: config.username, + password: config.password, + client: connectionSettings, + level: AccessLevel.ADMINISTRATOR, + }).then( + () => { + this.setState({isSubmitting: false}, () => history.replace('overview')); + }, + (error: Error) => { + this.setState({isSubmitting: false, errorMessage: error.message}); + }, + ); + } + }; + + render() { + const {errorMessage, isSubmitting} = this.state; + const {intl, mode} = this.props; + const isLoginMode = mode === 'login'; + + return ( +
    + +
    { + this.formRef = ref; + }}> + +

    {this.getHeaderText()}

    +
    + +

    {this.getIntroText()}

    + {errorMessage != null ? ( + + {errorMessage} + + ) : null} + + + + + + +
    + {isLoginMode ? null : ( + + + + )} + + + + + + +
    +
    +
    + ); + } +} + +export default injectIntl(AuthForm); diff --git a/client/src/javascript/components/general/Badge.js b/client/src/javascript/components/general/Badge.js deleted file mode 100644 index 099a33fe7..000000000 --- a/client/src/javascript/components/general/Badge.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -export default class Badge extends React.Component { - render() { - return
    {this.props.children}
    ; - } -} diff --git a/client/src/javascript/components/general/Badge.tsx b/client/src/javascript/components/general/Badge.tsx new file mode 100644 index 000000000..537b6697d --- /dev/null +++ b/client/src/javascript/components/general/Badge.tsx @@ -0,0 +1,9 @@ +import {FC, ReactNode} from 'react'; + +interface BadgeProps { + children: ReactNode; +} + +const Badge: FC = ({children}: BadgeProps) =>
    {children}
    ; + +export default Badge; diff --git a/client/src/javascript/components/general/ClientConnectionInterruption.js b/client/src/javascript/components/general/ClientConnectionInterruption.js deleted file mode 100644 index bb5921fa9..000000000 --- a/client/src/javascript/components/general/ClientConnectionInterruption.js +++ /dev/null @@ -1,189 +0,0 @@ -import { - Button, - Form, - FormError, - FormRow, - FormRowItem, - Panel, - PanelContent, - PanelHeader, - PanelFooter, -} from 'flood-ui-kit'; -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -import AuthActions from '../../actions/AuthActions'; -import AuthStore from '../../stores/AuthStore'; -import Checkmark from '../icons/Checkmark'; -import ClientActions from '../../actions/ClientActions'; -import connectStores from '../../util/connectStores'; -import EventTypes from '../../constants/EventTypes'; -import FloodActions from '../../actions/FloodActions'; -import RtorrentConnectionTypeSelection from './RtorrentConnectionTypeSelection'; - -class ClientConnectionInterruption extends React.Component { - state = { - hasTestedConnection: false, - isConnectionVerified: false, - isTestingConnection: false, - }; - - handleFormChange = () => { - if (this.state.hasTestedConnection) { - this.setState({ - isConnectionVerified: false, - hasTestedConnection: false, - }); - } - }; - - handleFormSubmit = ({formData}) => { - AuthActions.updateUser(AuthStore.getCurrentUsername(), formData) - .then(() => { - FloodActions.restartActivityStream(); - }) - .catch(() => {}); - }; - - handleTestButtonClick = () => { - if (this.state.isTestingConnection) return; - const formData = this.formRef.getFormData(); - - this.setState( - { - isTestingConnection: true, - }, - () => { - ClientActions.testClientConnectionSettings(formData) - .then(() => { - this.setState({ - hasTestedConnection: true, - isConnectionVerified: true, - isTestingConnection: false, - }); - }) - .catch(() => { - this.setState({ - hasTestedConnection: true, - isConnectionVerified: false, - isTestingConnection: false, - }); - }); - }, - ); - }; - - renderConnectionTestResult() { - const {hasTestedConnection, isConnectionVerified} = this.state; - if (!hasTestedConnection || !isConnectionVerified) return null; - return ( - - - - - - - ); - } - - renderFormError() { - const {hasTestedConnection, isConnectionVerified, isTestingConnection} = this.state; - if (hasTestedConnection && !isConnectionVerified) { - return ( - - - - - - ); - } - } - - render() { - const {isAdmin, isInitialUser} = this.props; - const {isConnectionVerified, isTestingConnection} = this.state; - - if (!isAdmin && !isInitialUser) { - return ( - - -

    - -

    -
    - -

    - -

    -
    -
    - ); - } - - return ( - -
    { - this.formRef = ref; - }}> - -

    - -

    -
    - -

    - -

    - {this.renderFormError()} - -
    - - - {this.renderConnectionTestResult()} - - - - -
    -
    - ); - } -} - -const ConnectedClientConnectionInterruption = connectStores(ClientConnectionInterruption, () => { - return [ - { - store: AuthStore, - event: [ - EventTypes.AUTH_LOGIN_SUCCESS, - EventTypes.AUTH_REGISTER_SUCCESS, - EventTypes.AUTH_VERIFY_SUCCESS, - EventTypes.AUTH_VERIFY_ERROR, - ], - getValue: ({store}) => { - return { - isAdmin: store.isAdmin(), - isInitialUser: store.getIsInitialUser(), - }; - }, - }, - ]; -}); - -export default ConnectedClientConnectionInterruption; diff --git a/client/src/javascript/components/general/ClientConnectionInterruption.tsx b/client/src/javascript/components/general/ClientConnectionInterruption.tsx new file mode 100644 index 000000000..6f364853a --- /dev/null +++ b/client/src/javascript/components/general/ClientConnectionInterruption.tsx @@ -0,0 +1,124 @@ +import {FC, ReactText, useRef, useState} from 'react'; +import {FormattedMessage} from 'react-intl'; +import {observer} from 'mobx-react'; + +import { + Button, + Form, + FormError, + FormRow, + Panel, + PanelContent, + PanelHeader, + PanelFooter, + Select, + SelectItem, +} from '../../ui'; +import AuthActions from '../../actions/AuthActions'; +import AuthStore from '../../stores/AuthStore'; +import ClientActions from '../../actions/ClientActions'; +import ClientConnectionSettingsForm from './connection-settings/ClientConnectionSettingsForm'; +import FloodActions from '../../actions/FloodActions'; + +import type {ClientConnectionSettingsFormType} from './connection-settings/ClientConnectionSettingsForm'; + +const ClientConnectionInterruption: FC = observer(() => { + const [error, setError] = useState(null); + const [isSubmitting, setSubmitting] = useState(false); + const [selection, setSelection] = useState('retry'); + const settingsFormRef = useRef(null); + + return ( + +
    { + setSubmitting(true); + + if (selection === 'config') { + const currentUsername = AuthStore.currentUser.username; + const connectionSettings = settingsFormRef.current?.getConnectionSettings(); + + if (currentUsername == null || connectionSettings == null) { + setError('connection.settings.error.empty'); + setSubmitting(false); + return; + } + + try { + await AuthActions.updateUser(currentUsername, { + client: connectionSettings, + }) + .then(() => { + // do nothing. + }) + .catch((e) => { + setError('general.error.unknown'); + throw e; + }); + } catch { + setSubmitting(false); + return; + } + } + + await ClientActions.testConnection() + .then(() => { + FloodActions.restartActivityStream(); + }) + .catch(() => { + setError('connection-interruption.verification-error'); + }); + + setSubmitting(false); + }}> + +

    + +

    +
    + + {error && ( + + + + + + )} + {AuthStore.currentUser.isAdmin ? ( + + + + ) : ( +

    + +

    + )} + {selection === 'config' && } +
    + + + {selection === 'retry' && ( + + )} + {selection === 'config' && ( + + )} + + +
    +
    + ); +}); + +export default ClientConnectionInterruption; diff --git a/client/src/javascript/components/general/ContextMenuMountPoint.tsx b/client/src/javascript/components/general/ContextMenuMountPoint.tsx new file mode 100644 index 000000000..ef33bffb3 --- /dev/null +++ b/client/src/javascript/components/general/ContextMenuMountPoint.tsx @@ -0,0 +1,109 @@ +import classnames from 'classnames'; +import {FC} from 'react'; +import {observer} from 'mobx-react'; +import {useIntl} from 'react-intl'; +import {useKeyPressEvent} from 'react-use'; + +import {ContextMenu} from '../../ui'; +import UIActions from '../../actions/UIActions'; +import UIStore from '../../stores/UIStore'; + +import type {ActiveContextMenu} from '../../stores/UIStore'; + +interface ContextMenuMountPointProps { + id: ActiveContextMenu['id']; +} + +const ContextMenuMountPoint: FC = observer(({id}: ContextMenuMountPointProps) => { + const isOpen = UIStore.activeContextMenu?.id === id; + const items = UIStore.activeContextMenu?.items ?? []; + const triggerCoordinates = UIStore.activeContextMenu?.clickPosition ?? { + x: 0, + y: 0, + }; + + const intl = useIntl(); + + useKeyPressEvent('Escape', () => UIActions.dismissContextMenu(id)); + + return ( + { + UIActions.dismissContextMenu(id); + }} + onOverlayRightClick={(e) => { + e.preventDefault(); + }} + isIn={isOpen}> + {items.map((item, index) => { + let menuItemContent; + let menuItemClasses; + + switch (item.type) { + case 'action': + menuItemClasses = classnames('menu__item', { + 'is-selectable': item.clickHandler, + }); + menuItemContent = ( + + + {intl.formatMessage({id: item.label})} + {item.labelAction ? ( + + + + ) : undefined} + + {item.labelSecondary ? ( + + + + ) : undefined} + + ); + break; + case 'separator': + default: + menuItemClasses = classnames('menu__item', { + 'menu__item--separator': item.type === 'separator', + }); + break; + } + + return ( +
  • { + if (item.type !== 'separator') { + if (item.dismissMenu === false) { + event.nativeEvent.stopImmediatePropagation(); + } + + if (item.clickHandler) { + item.clickHandler(event); + } + + if (item.dismissMenu !== false) { + UIActions.dismissContextMenu(id); + } + } + + return false; + }} + onContextMenu={(event) => { + event.preventDefault(); + }}> + {menuItemContent} +
  • + ); + })} +
    + ); +}); + +export default ContextMenuMountPoint; diff --git a/client/src/javascript/components/general/CustomScrollbars.js b/client/src/javascript/components/general/CustomScrollbars.js deleted file mode 100644 index 79c82b8cb..000000000 --- a/client/src/javascript/components/general/CustomScrollbars.js +++ /dev/null @@ -1,90 +0,0 @@ -import classnames from 'classnames'; -import React from 'react'; -import {Scrollbars} from 'react-custom-scrollbars'; - -const METHODS_TO_BIND = ['getHorizontalThumb', 'getVerticalThumb']; - -export default class CustomScrollbar extends React.Component { - scrollbarRef = null; - - constructor() { - super(); - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - getHorizontalThumb(props) { - if (this.props.getHorizontalThumb) { - return this.props.getHorizontalThumb(props, this.props.onThumbMouseUp); - } - - return ( -
    - ); - } - - getVerticalThumb(props) { - if (this.props.getVerticalThumb) { - return this.props.getVerticalThumb(props, this.props.onThumbMouseUp); - } - - return ( -
    - ); - } - - renderView(props) { - return ( -
    - {props.children} -
    - ); - } - - render() { - const { - children, - className, - inverted, - nativeScrollHandler, - scrollHandler, - /* eslint-disable @typescript-eslint/no-unused-vars */ - getHorizontalThumb: _getHorizontalThumb, - getVerticalThumb: _getVerticalThumb, - /* eslint-enable @typescript-eslint/no-unused-vars */ - ...otherProps - } = this.props; - const classes = classnames('scrollbars', className, { - 'is-inverted': inverted, - }); - - return ( - { - this.scrollbarRef = ref; - }} - renderView={this.renderView} - renderThumbHorizontal={this.getHorizontalThumb} - renderThumbVertical={this.getVerticalThumb} - onScroll={nativeScrollHandler} - onScrollFrame={scrollHandler} - {...otherProps}> - {children} - - ); - } -} - -CustomScrollbar.defaultProps = { - className: '', - inverted: false, - nativeScrollHandler: null, - scrollHandler: null, -}; diff --git a/client/src/javascript/components/general/Duration.js b/client/src/javascript/components/general/Duration.js deleted file mode 100644 index 7dfa1f07e..000000000 --- a/client/src/javascript/components/general/Duration.js +++ /dev/null @@ -1,114 +0,0 @@ -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -export default class Duration extends React.Component { - render() { - let {suffix = null} = this.props; - const {value: duration} = this.props; - - if (duration == null) { - return null; - } - - let content = null; - - if (suffix) { - suffix = {suffix}; - } - - if (duration === 'Infinity') { - content = ; - } else if (duration.years > 0) { - content = [ - - {duration.years} - - - - , - - {duration.weeks} - - - - , - ]; - } else if (duration.weeks > 0) { - content = [ - - {duration.weeks} - - - - , - - {duration.days} - - - - , - ]; - } else if (duration.days > 0) { - content = [ - - {duration.days} - - - - , - - {duration.hours} - - - - , - ]; - } else if (duration.hours > 0) { - content = [ - - {duration.hours} - - - - , - - {duration.minutes} - - - - , - ]; - } else if (duration.minutes > 0) { - content = [ - - {duration.minutes} - - - - , - - {duration.seconds} - - - - , - ]; - } else { - content = ( - - {duration.seconds} - - - - - ); - } - - return ( - - {content} - {suffix} - - ); - } -} diff --git a/client/src/javascript/components/general/Duration.tsx b/client/src/javascript/components/general/Duration.tsx new file mode 100644 index 000000000..23c867d75 --- /dev/null +++ b/client/src/javascript/components/general/Duration.tsx @@ -0,0 +1,161 @@ +import {FC, ReactNode} from 'react'; +import {FormattedMessage} from 'react-intl'; + +const secondsToDuration = ( + cumSeconds: number, +): { + years?: number; + weeks?: number; + days?: number; + hours?: number; + minutes?: number; + seconds?: number; + cumSeconds: number; +} => { + const years = Math.floor(cumSeconds / 31536000); + const weeks = Math.floor((cumSeconds % 31536000) / 604800); + const days = Math.floor(((cumSeconds % 31536000) % 604800) / 86400); + const hours = Math.floor((((cumSeconds % 31536000) % 604800) % 86400) / 3600); + const minutes = Math.floor(((((cumSeconds % 31536000) % 604800) % 86400) % 3600) / 60); + const seconds = Math.floor(cumSeconds - minutes * 60); + let timeRemaining = null; + + if (years > 0) { + timeRemaining = {years, weeks, cumSeconds}; + } else if (weeks > 0) { + timeRemaining = {weeks, days, cumSeconds}; + } else if (days > 0) { + timeRemaining = {days, hours, cumSeconds}; + } else if (hours > 0) { + timeRemaining = {hours, minutes, cumSeconds}; + } else if (minutes > 0) { + timeRemaining = {minutes, seconds, cumSeconds}; + } else { + timeRemaining = {seconds, cumSeconds}; + } + + return timeRemaining; +}; + +interface DurationProps { + suffix?: ReactNode; + value: number; +} + +const Duration: FC = (props: DurationProps) => { + const {suffix, value} = props; + + if (value == null) { + return null; + } + + let content = null; + let suffixElement = null; + + if (suffix) { + suffixElement = {suffix}; + } + + const duration = value === -1 ? -1 : secondsToDuration(value); + + if (duration === -1) { + content = ; + } else if (duration.years != null && duration.years > 0) { + content = [ + + {duration.years} + + + + , + + {duration.weeks} + + + + , + ]; + } else if (duration.weeks != null && duration.weeks > 0) { + content = [ + + {duration.weeks} + + + + , + + {duration.days} + + + + , + ]; + } else if (duration.days != null && duration.days > 0) { + content = [ + + {duration.days} + + + + , + + {duration.hours} + + + + , + ]; + } else if (duration.hours != null && duration.hours > 0) { + content = [ + + {duration.hours} + + + + , + + {duration.minutes} + + + + , + ]; + } else if (duration.minutes != null && duration.minutes > 0) { + content = [ + + {duration.minutes} + + + + , + + {duration.seconds} + + + + , + ]; + } else { + content = ( + + {duration.seconds} + + + + + ); + } + + return ( + + {content} + {suffixElement} + + ); +}; + +Duration.defaultProps = { + suffix: undefined, +}; + +export default Duration; diff --git a/client/src/javascript/components/general/GlobalContextMenuMountPoint.js b/client/src/javascript/components/general/GlobalContextMenuMountPoint.js deleted file mode 100644 index 2754c19d2..000000000 --- a/client/src/javascript/components/general/GlobalContextMenuMountPoint.js +++ /dev/null @@ -1,175 +0,0 @@ -import classnames from 'classnames'; -import {ContextMenu} from 'flood-ui-kit'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import EventTypes from '../../constants/EventTypes'; -import UIActions from '../../actions/UIActions'; -import UIStore from '../../stores/UIStore'; - -class GlobalContextMenuMountPoint extends React.Component { - static propTypes = { - onMenuClose: PropTypes.func, - onMenuOpen: PropTypes.func, - id: PropTypes.string.isRequired, - width: PropTypes.number, - }; - - static defaultProps = { - width: 200, - }; - - state = { - clickPosition: {}, - isOpen: false, - items: [], - }; - - componentDidMount() { - UIStore.listen(EventTypes.UI_CONTEXT_MENU_CHANGE, this.handleContextMenuChange); - } - - shouldComponentUpdate(nextProps, nextState) { - if (!this.state.isOpen && !nextState.isOpen) { - return false; - } - - if (this.state.isOpen !== nextState.isOpen) { - return true; - } - - let shouldUpdate = true; - - if ( - this.state.clickPosition.x === nextState.clickPosition.x && - this.state.clickPosition.y === nextState.clickPosition.y - ) { - shouldUpdate = false; - } - - if (!shouldUpdate) { - return this.state.items.some((item, index) => item !== nextState.items[index]); - } - - return shouldUpdate; - } - - componentWillUnmount() { - UIStore.unlisten(EventTypes.UI_CONTEXT_MENU_CHANGE, this.handleContextMenuChange); - } - - comnponentDidUpdate(prevProps, prevState) { - if (!prevState.isOpen && this.state.isOpen) { - global.document.addEventListener('keydown', this.handleKeyPress); - - if (prevProps.onMenuOpen) { - prevProps.onMenuOpen(); - } - } else if (prevState.isOpen && !this.state.isOpen) { - global.document.removeEventListener('keydown', this.handleKeyPress); - - if (prevProps.onMenuClose) { - prevProps.onMenuClose(); - } - } - } - - getMenuItems() { - return this.state.items.map((item, index) => { - let labelAction; - let labelSecondary; - let menuItemContent; - - const menuItemClasses = classnames('menu__item', { - 'is-selectable': item.clickHandler, - 'menu__item--separator': item.type === 'separator', - }); - const primaryLabelClasses = classnames('menu__item__label--primary', { - 'has-action': item.labelAction, - }); - - if (item.labelSecondary) { - labelSecondary = {item.labelSecondary}; - } - - if (item.labelAction) { - labelAction = {item.labelAction}; - } - - if (item.type !== 'separator') { - menuItemContent = ( - - - {item.label} - {labelAction} - - {labelSecondary} - - ); - } - - return ( - // TODO: Find a better key for this - // eslint-disable-next-line react/no-array-index-key -
  • - {menuItemContent} -
  • - ); - }); - } - - handleContextMenuChange = () => { - const activeContextMenu = UIStore.getActiveContextMenu(); - - if (activeContextMenu != null && activeContextMenu.id === this.props.id) { - this.setState({ - isOpen: true, - clickPosition: activeContextMenu.clickPosition, - items: activeContextMenu.items, - }); - } else if (this.state.isOpen) { - this.setState({ - isOpen: false, - }); - } - }; - - handleKeyPress = event => { - if (event.key === 'Escape') { - UIActions.dismissContextMenu(this.props.id); - } - }; - - handleMenuItemClick(item, event) { - if (item.dismissMenu === false) { - event.nativeEvent.stopImmediatePropagation(); - } - - if (item.clickHandler) { - item.clickHandler(item.action, event); - } - - if (item.dismissMenu !== false) { - UIActions.dismissContextMenu(this.props.id); - } - - return false; - } - - handleOverlayClick = () => { - UIActions.dismissContextMenu(this.props.id); - }; - - render() { - return ( - - {this.getMenuItems()} - - ); - } -} - -export default GlobalContextMenuMountPoint; diff --git a/client/src/javascript/components/general/Icon.scss b/client/src/javascript/components/general/Icon.scss deleted file mode 100644 index ae3518afd..000000000 --- a/client/src/javascript/components/general/Icon.scss +++ /dev/null @@ -1,17 +0,0 @@ -.icon { - display: block; -} - -.sizeSmall { - height: 12px; - width: 12px; -} - -.sizeMedium { - height: 16px; - width: 16px; -} - -.fillCurrentColor { - fill: currentColor; -} diff --git a/client/src/javascript/components/general/Icon.scss.d.ts b/client/src/javascript/components/general/Icon.scss.d.ts deleted file mode 100644 index 51e281714..000000000 --- a/client/src/javascript/components/general/Icon.scss.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare const styles: { - readonly icon: string; - readonly sizeSmall: string; - readonly sizeMedium: string; - readonly fillCurrentColor: string; -}; -export = styles; diff --git a/client/src/javascript/components/general/Icon.tsx b/client/src/javascript/components/general/Icon.tsx deleted file mode 100644 index b64c213f1..000000000 --- a/client/src/javascript/components/general/Icon.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import classnames from 'classnames'; -import * as React from 'react'; -import * as styles from './Icon.scss'; - -enum Fills { - NONE, - CURRENT_COLOR, -} - -enum Sizes { - SMALL, - MEDIUM, -} - -interface Props { - className?: string; - fill: Fills; - glyph: string; - size: Sizes; -} - -export default class Icon extends React.PureComponent { - public static defaultProps = { - fill: Fills.CURRENT_COLOR, - size: Sizes.MEDIUM, - }; - - public static Fills = Fills; - - public static Sizes = Sizes; - - public render(): React.ReactNode { - const {className, fill, glyph, size, ...restProps} = this.props; - - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/general/ListViewport.js b/client/src/javascript/components/general/ListViewport.js deleted file mode 100644 index 5e153099e..000000000 --- a/client/src/javascript/components/general/ListViewport.js +++ /dev/null @@ -1,277 +0,0 @@ -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import CustomScrollbars from './CustomScrollbars'; - -const methodsToBind = [ - 'getListPadding', - 'getViewportLimits', - 'handleScroll', - 'handleScrollStart', - 'handleScrollStop', - 'measureItemHeight', - 'scrollToTop', - 'setScrollPosition', - 'setViewportHeight', -]; - -class ListViewport extends React.Component { - static propTypes = { - bottomSpacerClass: PropTypes.string, - itemRenderer: PropTypes.func.isRequired, - itemScrollOffset: PropTypes.number, - listClass: PropTypes.string, - listLength: PropTypes.number.isRequired, - scrollContainerClass: PropTypes.string, - topSpacerClass: PropTypes.string, - }; - - static defaultProps = { - bottomSpacerClass: 'list__spacer list__spacer--bottom', - itemScrollOffset: 10, - topSpacerClass: 'list__spacer list__spacer--top', - }; - - constructor() { - super(); - - this.isScrolling = false; - this.lastScrollTop = 0; - this.nodeRefs = {}; - this.state = { - itemHeight: null, - listVerticalPadding: null, - scrollTop: 0, - viewportHeight: null, - }; - - methodsToBind.forEach(method => { - this[method] = this[method].bind(this); - }); - - this.setViewportHeight = _.debounce(this.setViewportHeight, 250); - this.updateAfterScrolling = _.debounce(this.updateAfterScrolling, 500, { - leading: true, - trailing: true, - }); - this.setScrollPosition = _.throttle(this.setScrollPosition, 100); - } - - componentDidMount() { - global.addEventListener('resize', this.setViewportHeight); - this.setViewportHeight(); - } - - shouldComponentUpdate(nextProps, nextState) { - const scrollDelta = Math.abs(this.state.scrollTop - nextState.scrollTop); - - if (this.isScrolling && scrollDelta > 20) { - return false; - } - - return true; - } - - componentDidUpdate() { - const {nodeRefs, state} = this; - - if (state.itemHeight == null && nodeRefs.topSpacer != null) { - // TODO: Decouple this - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ - itemHeight: nodeRefs.topSpacer.nextSibling.offsetHeight, - }); - } - - if (state.listVerticalPadding == null && nodeRefs.list != null) { - const listStyle = global.getComputedStyle(nodeRefs.list); - const paddingBottom = Number(listStyle['padding-bottom'].replace('px', '')); - const paddingTop = Number(listStyle['padding-top'].replace('px', '')); - - // TODO: Decouple this - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ - listVerticalPadding: paddingBottom + paddingTop, - }); - } - } - - componentWillUnmount() { - global.removeEventListener('resize', this.setViewportHeight); - } - - getViewportLimits(scrollDelta) { - if (this.state.itemHeight == null) { - return { - minItemIndex: 0, - maxItemIndex: Math.min(50, this.props.listLength), - }; - } - - // Calculate the number of items that should be rendered based on the height - // of the viewport. We offset this to render a few more outide of the - // container's dimensions, which looks nicer when the user scrolls. - const {itemScrollOffset} = this.props; - const offsetBottom = scrollDelta > 0 ? itemScrollOffset * 2 : itemScrollOffset / 2; - const offsetTop = scrollDelta < 0 ? itemScrollOffset * 2 : itemScrollOffset / 2; - - const {itemHeight, listVerticalPadding, scrollTop} = this.state; - let {viewportHeight} = this.state; - - if (listVerticalPadding) { - viewportHeight -= listVerticalPadding; - } - - // The number of elements in view is the height of the viewport divided - // by the height of the elements. - const elementsInView = Math.ceil(viewportHeight / itemHeight); - - // The minimum item index to render is the number of items above the - // viewport's current scroll position, minus the offset. - const minItemIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - offsetTop); - - // The maximum item index to render is the minimum item rendered, plus the - // number of items in view, plus double the offset. - const maxItemIndex = Math.min(this.props.listLength, minItemIndex + elementsInView + offsetBottom + offsetTop); - - return {minItemIndex, maxItemIndex}; - } - - handleScroll(scrollValues) { - this.setScrollPosition(scrollValues); - } - - handleScrollStart() { - this.isScrolling = true; - } - - handleScrollStop() { - this.isScrolling = false; - this.updateAfterScrolling(); - } - - measureItemHeight() { - this.lastScrollTop = 0; - - this.setState( - { - scrollTop: 0, - itemHeight: null, - }, - () => { - this.nodeRefs.outerScrollbar.scrollbarRef.scrollTop(0); - }, - ); - } - - getListPadding(minItemIndex, maxItemIndex, itemCount) { - const {itemHeight} = this.state; - - if (itemHeight == null) { - return {bottom: 0, top: 0}; - } - - // Calculate the number of pixels to pad the visible item list. - // If the minimum item index is less than 0, then we're already at the top - // of the list and don't need to render any padding. - if (minItemIndex < 0) { - minItemIndex = 0; - } - - // If the max item index is larger than the item count, then we're at the - // bottom of the list and don't need to render any padding. - if (maxItemIndex > itemCount) { - maxItemIndex = itemCount; - } - - const bottom = (itemCount - maxItemIndex) * itemHeight; - const top = minItemIndex * itemHeight; - - return {bottom, top}; - } - - scrollToTop() { - if (this.state.scrollTop !== 0) { - if (this.nodeRefs.outerScrollbar != null) { - this.nodeRefs.outerScrollbar.scrollbarRef.scrollToTop(); - } - - this.lastScrollTop = 0; - this.setState({scrollTop: 0}); - } - } - - setScrollPosition(scrollValues) { - this.lastScrollTop = this.state.scrollTop; - this.setState({scrollTop: scrollValues.scrollTop}); - } - - setViewportHeight() { - const {nodeRefs} = this; - - if (nodeRefs.outerScrollbar) { - this.setState({ - viewportHeight: nodeRefs.outerScrollbar.scrollbarRef.getClientHeight(), - }); - } - } - - updateAfterScrolling() { - this.forceUpdate(); - } - - render() { - const {lastScrollTop, nodeRefs, props, state} = this; - const {minItemIndex, maxItemIndex} = this.getViewportLimits(state.scrollTop - lastScrollTop); - const listPadding = this.getListPadding(minItemIndex, maxItemIndex, props.listLength); - const list = []; - - // For loops are fast, and performance matters here. - for (let index = minItemIndex; index < maxItemIndex; index++) { - list.push(props.itemRenderer(index)); - } - - const listContent = ( -
      { - nodeRefs.list = ref; - }}> -
    • { - nodeRefs.topSpacer = ref; - }} - style={{height: `${listPadding.top}px`}} - /> - {list} -
    • -
    - ); - - const scrollbarStyle = {}; - - if (props.width != null) { - scrollbarStyle.width = props.width; - } - - return ( - { - this.nodeRefs.outerScrollbar = ref; - }} - scrollHandler={this.handleScroll} - style={scrollbarStyle}> - {props.children} - {listContent} - - ); - } -} - -export default ListViewport; diff --git a/client/src/javascript/components/general/ListViewport.tsx b/client/src/javascript/components/general/ListViewport.tsx new file mode 100644 index 000000000..73a7a175c --- /dev/null +++ b/client/src/javascript/components/general/ListViewport.tsx @@ -0,0 +1,86 @@ +import {ComponentProps, FC, forwardRef, RefCallback, UIEvent, useEffect, useRef} from 'react'; +import {FixedSizeList} from 'react-window'; +import {OverlayScrollbarsComponent} from 'overlayscrollbars-react'; +import {useWindowSize} from 'react-use'; + +import type {ListChildComponentProps} from 'react-window'; + +import ConfigStore from '../../stores/ConfigStore'; + +const Overflow = forwardRef>((props: ComponentProps<'div'>, ref) => { + const {children, className, onScroll} = props; + const osRef = useRef(null); + + useEffect(() => { + const scrollbarRef = osRef.current; + + if (scrollbarRef == null) { + return () => { + // do nothing. + }; + } + + const viewport = scrollbarRef.osInstance()?.getElements().viewport as HTMLDivElement; + + const refCallback = ref as RefCallback; + refCallback(viewport); + + if (onScroll) { + viewport.addEventListener('scroll', (e) => onScroll((e as unknown) as UIEvent), { + passive: true, + }); + } + + return () => { + if (onScroll) { + viewport.removeEventListener('scroll', (e) => onScroll((e as unknown) as UIEvent)); + } + }; + }, [onScroll, ref]); + + return ( + + {children} + + ); +}); + +interface ListViewportProps { + className: string; + itemRenderer: FC; + itemSize: number; + listLength: number; + outerRef?: RefCallback; +} + +const ListViewport = forwardRef((props: ListViewportProps, ref) => { + const {className, itemRenderer, itemSize, listLength, outerRef} = props; + const {height: windowHeight, width: windowWidth} = useWindowSize(); + + return ( + 720 ? Overflow : undefined} // Don't use custom scrollbar on smaller screens + ref={ref} + outerRef={outerRef}> + {itemRenderer} + + ); +}); + +ListViewport.defaultProps = { + outerRef: undefined, +}; + +export default ListViewport; diff --git a/client/src/javascript/components/general/LoadingIndicator.js b/client/src/javascript/components/general/LoadingIndicator.js deleted file mode 100644 index bd41c901f..000000000 --- a/client/src/javascript/components/general/LoadingIndicator.js +++ /dev/null @@ -1,18 +0,0 @@ -import classnames from 'classnames'; -import React from 'react'; - -export default class LoadingIndicator extends React.Component { - render() { - const classes = classnames('loading-indicator', { - 'is-inverse': this.props.inverse, - }); - - return ( -
    -
    -
    -
    -
    - ); - } -} diff --git a/client/src/javascript/components/general/LoadingIndicator.tsx b/client/src/javascript/components/general/LoadingIndicator.tsx new file mode 100644 index 000000000..7bec4dd5f --- /dev/null +++ b/client/src/javascript/components/general/LoadingIndicator.tsx @@ -0,0 +1,28 @@ +import classnames from 'classnames'; +import {FC} from 'react'; + +interface LoadingIndicatorProps { + inverse?: boolean; +} + +const LoadingIndicator: FC = (props: LoadingIndicatorProps) => { + const {inverse} = props; + + const classes = classnames('loading-indicator', { + 'is-inverse': inverse, + }); + + return ( +
    +
    +
    +
    +
    + ); +}; + +LoadingIndicator.defaultProps = { + inverse: true, +}; + +export default LoadingIndicator; diff --git a/client/src/javascript/components/general/LoadingOverlay.tsx b/client/src/javascript/components/general/LoadingOverlay.tsx new file mode 100644 index 000000000..bb2f3b6bf --- /dev/null +++ b/client/src/javascript/components/general/LoadingOverlay.tsx @@ -0,0 +1,54 @@ +import classnames from 'classnames'; +import {FC} from 'react'; +import {useIntl} from 'react-intl'; + +import Checkmark from '../icons/Checkmark'; +import LoadingIndicator from './LoadingIndicator'; + +import type {Dependencies} from '../../stores/UIStore'; + +const ICONS = { + satisfied: , +}; + +const LoadingDependencyList: FC<{dependencies: Dependencies}> = ({dependencies}: {dependencies: Dependencies}) => { + const intl = useIntl(); + + return ( +
      + {Object.keys(dependencies).map((id: string) => { + const {message, satisfied} = dependencies[id]; + const statusIcon = ICONS.satisfied; + const classes = classnames('dependency-list__dependency', { + 'dependency-list__dependency--satisfied': satisfied, + }); + + return ( +
    • + {satisfied != null ? {statusIcon} : null} + + {typeof message === 'string' ? message : intl.formatMessage(message)} + +
    • + ); + })} +
    + ); +}; + +const LoadingOverlay: FC<{dependencies?: Dependencies}> = (props: {dependencies?: Dependencies}) => { + const {dependencies} = props; + + return ( +
    + + {dependencies != null ? : null} +
    + ); +}; + +LoadingOverlay.defaultProps = { + dependencies: undefined, +}; + +export default LoadingOverlay; diff --git a/client/src/javascript/components/general/NavigationList.js b/client/src/javascript/components/general/NavigationList.js deleted file mode 100644 index cd67750d0..000000000 --- a/client/src/javascript/components/general/NavigationList.js +++ /dev/null @@ -1,69 +0,0 @@ -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -class NavigationList extends React.Component { - static propTypes = { - defaultItem: PropTypes.string, - itemClassName: PropTypes.string, - items: PropTypes.array, - listClassName: PropTypes.string, - onChange: PropTypes.func, - selectedClassName: PropTypes.string, - uniqueClassName: PropTypes.string, - }; - - static defaultProps = { - itemClassName: 'navigation__item', - listClassName: 'navigation', - selectedClassName: 'is-active', - }; - - constructor() { - super(); - - this.state = { - selectedItem: null, - }; - } - - getNavigationItems(items) { - return items.map(item => { - let selectedSlug = null; - - if (this.state.selectedItem) { - selectedSlug = this.state.selectedItem; - } else { - selectedSlug = this.props.defaultItem; - } - - const classes = classnames(this.props.itemClassName, { - [this.props.selectedClassName]: item.slug === selectedSlug, - }); - - return ( -
  • - {item.label} -
  • - ); - }); - } - - handleItemClick(item) { - this.setState({ - selectedItem: item.slug, - }); - - this.props.onChange(item); - } - - render() { - const classes = classnames(this.props.listClassName, { - [this.props.uniqueClassName]: this.props.uniqueClassName, - }); - - return
      {this.getNavigationItems(this.props.items)}
    ; - } -} - -export default NavigationList; diff --git a/client/src/javascript/components/general/Portal.js b/client/src/javascript/components/general/Portal.js deleted file mode 100644 index 5aa46bc3f..000000000 --- a/client/src/javascript/components/general/Portal.js +++ /dev/null @@ -1,41 +0,0 @@ -import {IntlProvider} from 'react-intl'; -import PropTypes from 'prop-types'; -import React from 'react'; -import ReactDOM from 'react-dom'; - -import * as i18n from '../../i18n/languages'; -import SettingsStore from '../../stores/SettingsStore'; - -class Portal extends React.Component { - static propTypes = { - children: PropTypes.node, - }; - - static defaultProps = { - children:
    , - }; - - componentDidMount() { - this.nodeEl = document.createElement('div'); - document.body.appendChild(this.nodeEl); - } - - componentWillUnmount() { - ReactDOM.unmountComponentAtNode(this.nodeEl); - document.body.removeChild(this.nodeEl); - } - - render() { - const locale = SettingsStore.getFloodSettings('language'); - if (this.nodeEl == null) return null; - return ReactDOM.createPortal( - // eslint-disable-next-line import/namespace - - {this.props.children} - , - this.nodeEl, - ); - } -} - -export default Portal; diff --git a/client/src/javascript/components/general/PriorityMeter.tsx b/client/src/javascript/components/general/PriorityMeter.tsx new file mode 100644 index 000000000..f36186529 --- /dev/null +++ b/client/src/javascript/components/general/PriorityMeter.tsx @@ -0,0 +1,103 @@ +import {FC, ReactNode, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import PriorityLevels from '../../constants/PriorityLevels'; + +interface PriorityMeterProps { + id: string | number; + level: number; + maxLevel: number; + priorityType: keyof typeof PriorityLevels; + showLabel?: boolean; + clickHandled?: boolean; + changePriorityFuncRef?: React.MutableRefObject<() => number>; + onChange: (id: this['id'], level: this['level']) => void; +} + +const PriorityMeter: FC = ({ + id, + level, + maxLevel, + priorityType, + showLabel, + clickHandled, + changePriorityFuncRef, + onChange, +}: PriorityMeterProps) => { + const intl = useIntl(); + const [priorityLevel, setPriorityLevel] = useState(level); + + const changePriority = () => { + let newLevel = priorityLevel; + + if (newLevel >= maxLevel) { + newLevel = 0; + } else { + newLevel += 1; + } + + setPriorityLevel(newLevel); + onChange(id, newLevel); + + return newLevel; + }; + + if (changePriorityFuncRef != null) { + // eslint-disable-next-line no-param-reassign + changePriorityFuncRef.current = changePriority; + } + + let labelElement: ReactNode; + if (showLabel) { + const priorityLevels = PriorityLevels[priorityType]; + + let priorityLevelElement: ReactNode; + switch (priorityLevels[priorityLevel as keyof typeof priorityLevels]) { + case 'DONT_DOWNLOAD': + priorityLevelElement = intl.formatMessage({ + id: 'priority.dont.download', + }); + break; + case 'HIGH': + priorityLevelElement = intl.formatMessage({ + id: 'priority.high', + }); + break; + case 'LOW': + priorityLevelElement = intl.formatMessage({ + id: 'priority.low', + }); + break; + default: + priorityLevelElement = intl.formatMessage({ + id: 'priority.normal', + }); + break; + } + + labelElement = {priorityLevelElement}; + } + + return ( +
    { + changePriority(); + } + }> +
    + {labelElement} +
    + ); +}; + +PriorityMeter.defaultProps = { + showLabel: false, + clickHandled: false, + changePriorityFuncRef: undefined, +}; + +export default PriorityMeter; diff --git a/client/src/javascript/components/general/ProgressBar.js b/client/src/javascript/components/general/ProgressBar.js deleted file mode 100644 index 714398b8f..000000000 --- a/client/src/javascript/components/general/ProgressBar.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -export default class ProgressBar extends React.PureComponent { - render() { - const percent = Math.round(this.props.percent); - const style = {}; - - if (percent !== 100) { - style.width = `${percent}%`; - } - - return ( -
    -
    {this.props.icon}
    -
    -
    -
    -
    - ); - } -} diff --git a/client/src/javascript/components/general/ProgressBar.tsx b/client/src/javascript/components/general/ProgressBar.tsx new file mode 100644 index 000000000..f50328648 --- /dev/null +++ b/client/src/javascript/components/general/ProgressBar.tsx @@ -0,0 +1,21 @@ +import {FC, memo} from 'react'; + +interface ProgressBarProps { + percent: number; + icon?: JSX.Element; +} + +const ProgressBar: FC = memo(({percent, icon}: ProgressBarProps) => ( +
    +
    {icon}
    +
    +
    +
    +
    +)); + +ProgressBar.defaultProps = { + icon: undefined, +}; + +export default ProgressBar; diff --git a/client/src/javascript/components/general/Ratio.js b/client/src/javascript/components/general/Ratio.js deleted file mode 100644 index fa9ee28a5..000000000 --- a/client/src/javascript/components/general/Ratio.js +++ /dev/null @@ -1,21 +0,0 @@ -import {FormattedNumber} from 'react-intl'; -import React from 'react'; - -export default class Ratio extends React.Component { - render() { - let ratio = this.props.value; - - ratio /= 1000; - let precision = 1; - - if (ratio < 10) { - precision = 2; - } else if (ratio >= 100) { - precision = 0; - } - - ratio = ratio.toFixed(precision); - - return ; - } -} diff --git a/client/src/javascript/components/general/RtorrentConnectionTypeSelection.js b/client/src/javascript/components/general/RtorrentConnectionTypeSelection.js deleted file mode 100644 index ad116a86b..000000000 --- a/client/src/javascript/components/general/RtorrentConnectionTypeSelection.js +++ /dev/null @@ -1,102 +0,0 @@ -import {FormGroup, FormRow, Radio, Textbox} from 'flood-ui-kit'; -import {FormattedMessage, injectIntl} from 'react-intl'; -import PropTypes from 'prop-types'; -import React, {Component} from 'react'; - -class RtorrentConnectionTypeSelection extends Component { - static propTypes = { - onChange: PropTypes.func, - }; - - static defaultProps = { - onChange: () => {}, - }; - - state = { - connectionType: 'tcp', - }; - - handleTypeChange = event => { - if (this.state.connectionType !== event.target.value) { - this.setState({connectionType: event.target.value}); - } - - this.props.onChange(event.target.value); - }; - - renderConnectionOptions() { - if (this.state.connectionType === 'tcp') { - return ( - - } - placeholder={this.props.intl.formatMessage({ - id: 'auth.rtorrentHost', - defaultMessage: 'rTorrent Host', - })} - /> - } - placeholder={this.props.intl.formatMessage({ - id: 'auth.rtorrentPort', - defaultMessage: 'rTorrent Port', - })} - /> - - ); - } - - return ( - - } - placeholder={this.props.intl.formatMessage({ - id: 'auth.rtorrentSocketPath', - defaultMessage: 'rTorrent Socket Path', - })} - /> - - ); - } - - render() { - return ( - - - - - - - - - - - - - - - {this.renderConnectionOptions()} - - - ); - } -} - -export default injectIntl(RtorrentConnectionTypeSelection); diff --git a/client/src/javascript/components/general/Size.js b/client/src/javascript/components/general/Size.js deleted file mode 100644 index b3a29daae..000000000 --- a/client/src/javascript/components/general/Size.js +++ /dev/null @@ -1,47 +0,0 @@ -import {FormattedNumber, injectIntl} from 'react-intl'; -import React from 'react'; - -import {compute, getTranslationString} from '../../util/size'; - -class Size extends React.Component { - renderNumber(computedNumber) { - if (Number.isNaN(computedNumber.value)) { - return '—'; - } - - return ; - } - - render() { - const {value, isSpeed, className, precision, intl} = this.props; - const computed = compute(value, precision); - - let translatedUnit = intl.formatMessage({id: getTranslationString(computed.unit)}); - - if (isSpeed) { - translatedUnit = intl.formatMessage( - { - id: 'unit.speed', - defaultMessage: '{baseUnit}/s', - }, - { - baseUnit: translatedUnit, - }, - ); - } - - return ( - - {this.renderNumber(computed)} - {translatedUnit} - - ); - } -} - -Size.defaultProps = { - isSpeed: false, - precision: 2, -}; - -export default injectIntl(Size); diff --git a/client/src/javascript/components/general/Size.tsx b/client/src/javascript/components/general/Size.tsx new file mode 100644 index 000000000..1f8b343f2 --- /dev/null +++ b/client/src/javascript/components/general/Size.tsx @@ -0,0 +1,54 @@ +import {FC} from 'react'; +import {FormattedNumber, useIntl} from 'react-intl'; + +import {compute, getTranslationString} from '../../util/size'; + +const renderNumber = (computedNumber: ReturnType) => { + if (Number.isNaN(computedNumber.value)) { + return '—'; + } + + return ; +}; + +interface SizeProps { + value: number; + precision?: number; + isSpeed?: boolean; + className?: string; +} + +const Size: FC = ({value, isSpeed, className, precision}: SizeProps) => { + const computed = compute(value, precision); + const intl = useIntl(); + + let translatedUnit = intl.formatMessage({ + id: getTranslationString(computed.unit), + }); + + if (isSpeed) { + translatedUnit = intl.formatMessage( + { + id: 'unit.speed', + }, + { + baseUnit: translatedUnit, + }, + ); + } + + return ( + + {renderNumber(computed)} + {translatedUnit} + + ); +}; + +Size.defaultProps = { + isSpeed: false, + precision: 2, + className: undefined, +}; + +export default Size; diff --git a/client/src/javascript/components/general/SortableList.js b/client/src/javascript/components/general/SortableList.js deleted file mode 100644 index 2a83794d6..000000000 --- a/client/src/javascript/components/general/SortableList.js +++ /dev/null @@ -1,112 +0,0 @@ -import classnames from 'classnames'; -import {DragDropContext} from 'react-dnd'; -import {injectIntl} from 'react-intl'; -import HTML5Backend from 'react-dnd-html5-backend'; -import React from 'react'; - -import SortableListItemDragLayer from './SortableListItemDragLayer'; -import SortableListItem from './SortableListItem'; - -const methodsToBind = ['handleDrop', 'handleMove', 'handleMouseDown']; - -class SortableList extends React.Component { - constructor(props) { - super(props); - - this.sortableListRef = null; - this.state = { - listOffset: null, - items: props.items, - }; - - methodsToBind.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - static getDerivedStateFromProps(props) { - return {items: props.items}; - } - - handleDrop() { - if (this.props.onDrop) { - this.props.onDrop(this.state.items); - } - } - - handleMouseDown(event) { - if (this.sortableListRef != null) { - this.setState({ - listOffset: this.sortableListRef.getBoundingClientRect(), - }); - } - - if (this.props.onMouseDown) { - this.props.onMouseDown(event); - } - } - - handleMove(dragIndex, hoverIndex) { - const {items} = this.state; - const draggedItem = items[dragIndex]; - - // Remove the item being dragged. - items.splice(dragIndex, 1); - // Add the item being dragged in its new position. - items.splice(hoverIndex, 0, draggedItem); - - this.setState({items}); - - if (this.props.onMove) { - this.props.onMove(items); - } - } - - getItemList() { - const { - handleDrop, - handleMove, - state: {items}, - props: {lockedIDs, renderItem}, - } = this; - - return items.map((item, index) => { - const {id, visible} = item; - - return ( - - {renderItem(item, index)} - - ); - }); - } - - render() { - const classes = classnames('sortable-list', this.props.className); - - return ( -
      { - this.sortableListRef = ref; - }}> - - {this.getItemList()} -
    - ); - } -} - -export default DragDropContext(HTML5Backend)(injectIntl(SortableList)); diff --git a/client/src/javascript/components/general/SortableList.tsx b/client/src/javascript/components/general/SortableList.tsx new file mode 100644 index 000000000..a10b5f763 --- /dev/null +++ b/client/src/javascript/components/general/SortableList.tsx @@ -0,0 +1,123 @@ +import classnames from 'classnames'; +import {DndProvider} from 'react-dnd-multi-backend'; +import HTML5toTouch from 'react-dnd-multi-backend/dist/esm/HTML5toTouch'; +import {injectIntl, WrappedComponentProps} from 'react-intl'; +import * as React from 'react'; + +import SortableListItem from './SortableListItem'; + +export type ListItem = { + id: string; + visible: boolean; +}; + +interface SortableListProps extends WrappedComponentProps { + id: string; + className: string; + lockedIDs: Array; + items: Array; + isDraggable?: boolean; + renderItem: (item: ListItem, index: number) => void; + onMouseDown?: (event: React.MouseEvent) => void; + onMove?: (items: this['items']) => void; + onDrop?: (items: this['items']) => void; +} + +interface SortableListStates { + items: SortableListProps['items']; +} + +class SortableList extends React.Component { + sortableListRef: HTMLUListElement | null = null; + + constructor(props: SortableListProps) { + super(props); + + this.state = { + items: props.items, + }; + } + + static getDerivedStateFromProps(props: SortableListProps) { + return {items: props.items}; + } + + getItemList() { + const { + handleDrop, + handleMove, + state: {items}, + props: {id: listID, lockedIDs, isDraggable, renderItem}, + } = this; + + return items.map((item, index) => { + const {id, visible} = item; + + return ( + + {renderItem(item, index)} + + ); + }); + } + + handleDrop = () => { + const {onDrop} = this.props; + if (onDrop) { + onDrop(this.state.items); + } + }; + + handleMouseDown = (event: React.MouseEvent) => { + const {onMouseDown} = this.props; + if (onMouseDown) { + onMouseDown(event); + } + }; + + handleMove = (dragIndex: number, hoverIndex: number) => { + const {onMove} = this.props; + const {items} = this.state; + const draggedItem = items[dragIndex]; + + // Remove the item being dragged. + items.splice(dragIndex, 1); + // Add the item being dragged in its new position. + items.splice(hoverIndex, 0, draggedItem); + + this.setState({items}); + + if (onMove) { + onMove(items); + } + }; + + render() { + const {className} = this.props; + const classes = classnames('sortable-list', className); + + return ( + +
      { + this.sortableListRef = ref; + }}> + {this.getItemList()} +
    +
    + ); + } +} + +export default injectIntl(SortableList); diff --git a/client/src/javascript/components/general/SortableListItem.js b/client/src/javascript/components/general/SortableListItem.js deleted file mode 100644 index dd3f392d8..000000000 --- a/client/src/javascript/components/general/SortableListItem.js +++ /dev/null @@ -1,104 +0,0 @@ -import _ from 'lodash'; -import classnames from 'classnames'; -import {DragSource, DropTarget} from 'react-dnd'; -import {getEmptyImage} from 'react-dnd-html5-backend'; -import React from 'react'; -import ReactDOM from 'react-dom'; - -import LockIcon from '../icons/LockIcon'; - -const itemSource = { - beginDrag({id, index, isVisible}) { - return {id, index, isVisible}; - }, - - canDrag({isLocked}) { - return !isLocked; - }, -}; - -const itemTarget = { - drop(props) { - if (props.onDrop) { - props.onDrop(); - } - }, - - hover(props, monitor, component) { - const dragIndex = monitor.getItem().index; - const {index: hoverIndex, isLocked} = props; - - // Don't replace items with themselves - if (isLocked || dragIndex === hoverIndex) { - return; - } - - // Determine rectangle on screen - // TODO: Add ref to component and use instead of findDOMNode - // eslint-disable-next-line react/no-find-dom-node - const hoverBoundingRect = ReactDOM.findDOMNode(component).getBoundingClientRect(); - - // Determine mouse position - const clientOffset = monitor.getClientOffset(); - - // Get the remaining pixels to the top of the list. - const hoverClientY = clientOffset.y - hoverBoundingRect.top; - - const isDraggingUp = dragIndex < hoverIndex; - const isDraggingDown = dragIndex > hoverIndex; - - const dragThreshhold = isDraggingDown ? hoverBoundingRect.height * 0.85 : hoverBoundingRect.height * 0.15; - - // Return early if we haven't dragged more than halfway past the next item. - if ((isDraggingUp && hoverClientY < dragThreshhold) || (isDraggingDown && hoverClientY > dragThreshhold)) { - return; - } - - props.onMove(dragIndex, hoverIndex); - monitor.getItem().index = hoverIndex; - }, -}; - -class SortableListItem extends React.Component { - componentDidMount() { - // Replace the native drag preview with an empty image. - this.props.connectDragPreview(getEmptyImage(), { - captureDraggingState: true, - }); - } - - render() { - const {children, isDragging, isLocked, connectDragSource, connectDropTarget} = this.props; - - let lockedIcon = null; - - if (isLocked) { - lockedIcon = ; - } - - const classes = classnames('sortable-list__item', { - 'sortable-list__item--is-dragging': isDragging, - 'sortable-list__item--is-locked': isLocked, - }); - - return connectDragSource( - connectDropTarget( -
    - {lockedIcon} - {children} -
    , - ), - ); - } -} - -export default _.flow([ - DragSource('globally-draggable-item', itemSource, (connect, monitor) => ({ - connectDragPreview: connect.dragPreview(), - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging(), - })), - DropTarget('globally-draggable-item', itemTarget, connect => ({ - connectDropTarget: connect.dropTarget(), - })), -])(SortableListItem); diff --git a/client/src/javascript/components/general/SortableListItem.tsx b/client/src/javascript/components/general/SortableListItem.tsx new file mode 100644 index 000000000..a0f05ea04 --- /dev/null +++ b/client/src/javascript/components/general/SortableListItem.tsx @@ -0,0 +1,111 @@ +import classnames from 'classnames'; +import {DragElementWrapper, DragPreviewOptions, DragSource, DragSourceOptions, DropTarget} from 'react-dnd'; +import {FC, ReactNode, useEffect} from 'react'; +import flow from 'lodash/flow'; +import {getEmptyImage} from 'react-dnd-html5-backend'; + +import LockIcon from '../icons/LockIcon'; + +interface SortableListItemProps { + children?: ReactNode; + list: string; + id: string; + index: number; + isVisible: boolean; + isDragging: boolean; + isLocked: boolean; + isDraggable: boolean; + onDrop: () => void; + onMove: (sourceIndex: number, targetIndex: number) => void; + connectDragPreview: DragElementWrapper; + connectDragSource: DragElementWrapper; + connectDropTarget: DragElementWrapper; +} + +const SortableListItem: FC = (props: SortableListItemProps) => { + const {children, isDragging, isLocked, connectDragPreview, connectDragSource, connectDropTarget} = props; + + useEffect(() => { + connectDragPreview(getEmptyImage(), { + captureDraggingState: true, + }); + }); + + let lockedIcon = null; + + if (isLocked) { + lockedIcon = ; + } + + const classes = classnames('sortable-list__item', { + 'sortable-list__item--is-dragging': isDragging, + 'sortable-list__item--is-locked': isLocked, + }); + + return connectDragSource( + connectDropTarget( +
    + {lockedIcon} + {children} +
    , + ), + ); +}; + +export default flow([ + DragSource( + 'globally-draggable-item', + { + beginDrag({list, id, index, isVisible}: SortableListItemProps) { + return {list, id, index, isVisible}; + }, + + canDrag({isLocked, isDraggable}: SortableListItemProps) { + if (isDraggable != null) { + return isDraggable; + } + + if (isLocked) { + return false; + } + + return true; + }, + }, + (connect, monitor) => ({ + connectDragPreview: connect.dragPreview(), + connectDragSource: connect.dragSource(), + isDragging: monitor.isDragging(), + }), + ), + DropTarget( + 'globally-draggable-item', + { + drop({onDrop}: SortableListItemProps) { + if (onDrop) { + onDrop(); + } + }, + + hover(props, monitor) { + const item: SortableListItemProps = monitor.getItem(); + + // Don't replace items with themselves + if (props.isLocked || item.index === props.index) { + return; + } + + // Don't drop item to another list + if (item.list !== props.list) { + return; + } + + props.onMove(item.index, props.index); + item.index = props.index; + }, + }, + (connect) => ({ + connectDropTarget: connect.dropTarget(), + }), + ), +])(SortableListItem); diff --git a/client/src/javascript/components/general/SortableListItemDragLayer.js b/client/src/javascript/components/general/SortableListItemDragLayer.js deleted file mode 100644 index b28d01029..000000000 --- a/client/src/javascript/components/general/SortableListItemDragLayer.js +++ /dev/null @@ -1,57 +0,0 @@ -import {DragLayer} from 'react-dnd'; -import {injectIntl} from 'react-intl'; -import PropTypes from 'prop-types'; -import React, {Component} from 'react'; - -const layerStyles = { - position: 'absolute', - pointerEvents: 'none', - zIndex: 100, - left: 0, - top: 0, - width: '100%', - height: '100%', -}; - -class SortableListItemDragLayer extends Component { - static propTypes = { - clientOffset: PropTypes.object, - differenceFromInitialOffset: PropTypes.object, - isDragging: PropTypes.bool.isRequired, - item: PropTypes.object, - }; - - getItemStyles = () => { - const {clientOffset, differenceFromInitialOffset, listOffset} = this.props; - - if (!clientOffset || !listOffset) { - return {display: 'none'}; - } - - const {x} = differenceFromInitialOffset; - const y = clientOffset.y - listOffset.top - 15; - - return {transform: `translate(${x}px, ${y}px)`}; - }; - - render() { - const {item, isDragging} = this.props; - - if (!isDragging) { - return null; - } - - return ( -
    -
    {this.props.renderItem({...item, dragIndicator: true})}
    -
    - ); - } -} - -export default DragLayer(monitor => ({ - clientOffset: monitor.getClientOffset(), - differenceFromInitialOffset: monitor.getDifferenceFromInitialOffset(), - isDragging: monitor.isDragging(), - item: monitor.getItem(), -}))(injectIntl(SortableListItemDragLayer)); diff --git a/client/src/javascript/components/general/Tooltip.js b/client/src/javascript/components/general/Tooltip.js deleted file mode 100644 index 2a460af8d..000000000 --- a/client/src/javascript/components/general/Tooltip.js +++ /dev/null @@ -1,358 +0,0 @@ -import _ from 'lodash'; -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import Portal from './Portal'; - -const ARROW_SIZE = 7; -const METHODS_TO_BIND = [ - 'dismissTooltip', - 'getIdealLocation', - 'handleMouseEnter', - 'handleMouseLeave', - 'handleTooltipMouseEnter', - 'handleTooltipMouseLeave', - 'isOpen', - 'triggerClose', -]; - -const TOOLTIP_PROPS = [ - 'align', - 'anchor', - 'children', - 'className', - 'contentClassName', - 'content', - 'elementTag', - 'interactive', - 'maxWidth', - 'onMouseLeave', - 'offset', - 'onClose', - 'onOpen', - 'position', - 'scrollContainer', - 'stayOpen', - 'suppress', - 'width', - 'wrapperClassName', - 'wrapText', -]; - -class Tooltip extends React.Component { - static propTypes = { - align: PropTypes.oneOf(['start', 'center', 'end']), - anchor: PropTypes.oneOf(['start', 'center', 'end']), - children: PropTypes.node, - className: PropTypes.string, - contentClassName: PropTypes.string, - content: PropTypes.node.isRequired, - elementTag: PropTypes.string, - interactive: PropTypes.bool, - maxWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - onMouseLeave: PropTypes.func, - offset: PropTypes.number, - onClose: PropTypes.func, - onOpen: PropTypes.func, - position: PropTypes.oneOf(['top', 'bottom', 'right', 'left']), - scrollContainer: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), - stayOpen: PropTypes.bool, - suppress: PropTypes.bool, - width: PropTypes.number, - wrapperClassName: PropTypes.string, - wrapText: PropTypes.bool, - }; - - static defaultProps = { - align: 'center', - anchor: 'center', - className: 'tooltip', - contentClassName: 'tooltip__content', - elementTag: 'div', - interactive: false, - offset: 0, - position: 'top', - scrollContainer: window, - stayOpen: false, - suppress: false, - wrapperClassName: 'tooltip__wrapper', - wrapText: false, - }; - - constructor() { - super(); - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - - this.container = null; - this.state = {isOpen: false, wasTriggeredClose: false}; - } - - componentWillUnmount() { - this.removeScrollListener(); - } - - handleMouseEnter(options = {}) { - const {props} = this; - - if (props.suppress && !options.forceOpen) { - return; - } - - const {anchor, position, coordinates} = this.getIdealLocation(props.anchor, props.position); - - this.setState({ - anchor, - isOpen: true, - position, - coordinates, - wasTriggeredClose: false, - }); - this.addScrollListener(); - - if (props.onOpen) { - props.onOpen(); - } - } - - handleMouseLeave() { - this.dismissTooltip(); - - if (this.props.onMouseLeave) { - this.props.onMouseLeave(); - } - } - - handleTooltipMouseEnter() { - if (this.props.interactive && !this.state.wasTriggeredClose) { - this.setState({isOpen: true}); - this.addScrollListener(); - } - } - - handleTooltipMouseLeave() { - this.dismissTooltip(); - } - - addScrollListener() { - if (!this.container) { - this.container = this.props.scrollContainer; - } - - this.container.addEventListener('scroll', this.dismissTooltip); - } - - dismissTooltip(options = {}) { - if ((!this.props.stayOpen || options.forceClose) && this.state.isOpen) { - this.setState({isOpen: false}); - this.removeScrollListener(); - - if (this.props.onClose) { - this.props.onClose(); - } - } - } - - getAnchor(isVertical, anchor, clearance, tooltipWidth, tooltipHeight) { - if (isVertical) { - return this.transformAnchor(anchor, clearance.left, clearance.right, tooltipWidth, clearance.boundingRect.width); - } - - return this.transformAnchor(anchor, clearance.top, clearance.bottom, tooltipHeight, clearance.boundingRect.height); - } - - getCoordinates(position, clearance, tooltipWidth, tooltipHeight) { - const {align, offset} = this.props; - let left = null; - let top = null; - - if (position === 'top' || position === 'bottom') { - if (align === 'center') { - left = clearance.boundingRect.left + clearance.boundingRect.width / 2; - } else if (align === 'start') { - // eslint-disable-next-line prefer-destructuring - left = clearance.boundingRect.left; - } else if (align === 'end') { - left = clearance.boundingRect.left + clearance.boundingRect.width - tooltipWidth; - } - } else { - top = clearance.boundingRect.top + clearance.boundingRect.height / 2; - } - - if (position === 'top') { - top = clearance.boundingRect.top - tooltipHeight + ARROW_SIZE + offset; - } else if (position === 'right') { - left = clearance.boundingRect.right + offset; - } else if (position === 'bottom') { - top = clearance.boundingRect.bottom + offset; - } else { - left = clearance.boundingRect.left - tooltipWidth + ARROW_SIZE + offset; - } - - return {left, top}; - } - - isVertical(position) { - return position !== 'left' && position !== 'right'; - } - - getPosition(position, clearance, tooltipWidth, tooltipHeight) { - // Change the position if the tooltip will be rendered off the screen. - if (position === 'left' && clearance.left < tooltipWidth) { - position = 'right'; - } else if (position === 'right' && clearance.right < tooltipWidth) { - position = 'left'; - } - - if (position === 'top' && clearance.top < tooltipHeight) { - position = 'bottom'; - } else if (position === 'bottom' && clearance.bottom < tooltipHeight && clearance.top > clearance.bottom) { - position = 'top'; - } - - return position; - } - - getIdealLocation(anchor, position) { - const clearance = this.getNodeClearance(this.triggerNode); - const isVertical = this.isVertical(position); - const tooltipRect = this.tooltipNode.getBoundingClientRect(); - const tooltipHeight = tooltipRect.height + ARROW_SIZE; - const tooltipWidth = tooltipRect.width + ARROW_SIZE; - - anchor = this.getAnchor(isVertical, anchor, clearance, tooltipWidth, tooltipHeight); - position = this.getPosition(position, clearance, tooltipWidth, tooltipHeight); - - const coordinates = this.getCoordinates(position, clearance, tooltipWidth, tooltipHeight); - - return {anchor, position, coordinates}; - } - - getNodeClearance(domNode) { - const viewportHeight = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0); - const viewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0); - const boundingRect = domNode.getBoundingClientRect(); - - return { - bottom: viewportHeight - boundingRect.bottom, - left: boundingRect.left, - right: viewportWidth - boundingRect.right, - top: boundingRect.top, - boundingRect, - }; - } - - isOpen() { - return this.state.isOpen; - } - - removeScrollListener() { - if (this.container) { - this.container.removeEventListener('scroll', this.dismissTooltip); - } - } - - triggerClose() { - this.setState({wasTriggeredClose: true}); - this.dismissTooltip({forceClose: true}); - } - - triggerOpen() { - this.handleMouseEnter({forceOpen: true}); - } - - transformAnchor(anchor, clearanceStart, clearanceEnd, tooltipDimension, triggerDimension) { - // Change the provided anchor based on the clearance available. - if (anchor === 'start' && clearanceEnd < tooltipDimension) { - return 'end'; - } - - if (anchor === 'end' && clearanceStart < tooltipDimension) { - return 'start'; - } - - if (anchor === 'center') { - const tooltipOverflow = (tooltipDimension - triggerDimension) / 2; - - if (clearanceStart < tooltipOverflow) { - return 'start'; - } - - if (clearanceEnd < tooltipOverflow) { - return 'end'; - } - } - - return anchor; - } - - render() { - const {props, state} = this; - let tooltipStyle = {}; - - const {align} = props; - // Get the anchor and position from state if possible. If not, get it from - // the props. - const anchor = state.anchor || props.anchor; - const position = state.position || props.position; - // Pass along any props that aren't specific to the Tooltip. - const elementProps = _.omit(props, TOOLTIP_PROPS); - - const tooltipClasses = classnames( - props.className, - `tooltip--anchor--${anchor}`, - `tooltip--position--${position}`, - `tooltip--align--${align}`, - { - 'is-interactive': props.interactive, - 'is-open': state.isOpen, - 'tooltip--no-wrap': !props.wrapText, - }, - ); - - if (state.coordinates) { - tooltipStyle = { - left: state.coordinates.left, - top: state.coordinates.top, - }; - } - - if (props.width) { - tooltipStyle.width = props.width; - } - - if (props.maxWidth) { - tooltipStyle.maxWidth = props.maxWidth; - } - - return ( - { - this.triggerNode = ref; - }}> - {props.children} - -
    { - this.tooltipNode = ref; - }} - style={tooltipStyle} - onMouseEnter={this.handleTooltipMouseEnter} - onMouseLeave={this.handleTooltipMouseLeave}> -
    {props.content}
    -
    -
    -
    - ); - } -} - -export default Tooltip; diff --git a/client/src/javascript/components/general/Tooltip.tsx b/client/src/javascript/components/general/Tooltip.tsx new file mode 100644 index 000000000..9ab6c2867 --- /dev/null +++ b/client/src/javascript/components/general/Tooltip.tsx @@ -0,0 +1,430 @@ +import classnames from 'classnames'; +import ReactDOM from 'react-dom'; +import * as React from 'react'; + +type Align = 'start' | 'center' | 'end'; + +type Position = 'top' | 'bottom' | 'right' | 'left'; + +type Coordinates = Record; + +type Clearance = Coordinates & { + boundingRect: Coordinates & { + width: number; + height: number; + }; +}; + +interface TooltipProps { + align?: Align; + anchor?: Align; + position?: Position; + offset?: number; + width: number; + maxWidth: number; + interactive?: boolean; + suppress?: boolean; + stayOpen?: boolean; + wrapText?: boolean; + className?: string; + contentClassName?: string; + wrapperClassName?: string; + content: React.ReactNode; + onOpen: () => void; + onClose: () => void; + onClick: () => void; + onMouseLeave: () => void; +} + +interface TooltipStates { + anchor?: Align; + coordinates?: Pick; + position?: Position; + isOpen: boolean; + wasTriggeredClose: boolean; +} + +const getNodeClearance = (domNode: HTMLDivElement) => { + const viewportHeight = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0); + const viewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0); + const boundingRect = domNode.getBoundingClientRect(); + + return { + bottom: viewportHeight - boundingRect.bottom, + left: boundingRect.left, + right: viewportWidth - boundingRect.right, + top: boundingRect.top, + boundingRect, + }; +}; + +const getPosition = ( + position: Position, + clearance: Clearance, + tooltipWidth: number, + tooltipHeight: number, +): Position => { + // Change the position if the tooltip will be rendered off the screen. + switch (position) { + case 'left': + if (clearance.left < tooltipWidth) { + return 'right'; + } + break; + case 'right': + if (clearance.right < tooltipWidth) { + return 'left'; + } + break; + case 'top': + if (clearance.top < tooltipHeight) { + return 'bottom'; + } + break; + case 'bottom': + if (clearance.bottom < tooltipHeight && clearance.top > clearance.bottom) { + return 'top'; + } + break; + default: + } + + return position; +}; + +const transformAnchor = ( + anchor: Align, + clearanceStart: number, + clearanceEnd: number, + tooltipDimension: number, + triggerDimension: number, +): Align => { + // Change the provided anchor based on the clearance available. + if (anchor === 'start' && clearanceEnd < tooltipDimension) { + return 'end'; + } + + if (anchor === 'end' && clearanceStart < tooltipDimension) { + return 'start'; + } + + if (anchor === 'center') { + const tooltipOverflow = (tooltipDimension - triggerDimension) / 2; + + if (clearanceStart < tooltipOverflow) { + return 'start'; + } + + if (clearanceEnd < tooltipOverflow) { + return 'end'; + } + } + + return anchor; +}; + +const getAnchor = ( + isVertical: boolean, + anchor: Align, + clearance: Clearance, + tooltipWidth: number, + tooltipHeight: number, +): Align => { + if (isVertical) { + return transformAnchor(anchor, clearance.left, clearance.right, tooltipWidth, clearance.boundingRect.width); + } + + return transformAnchor(anchor, clearance.top, clearance.bottom, tooltipHeight, clearance.boundingRect.height); +}; + +const ARROW_SIZE = 7; + +class Tooltip extends React.Component { + container = window; + triggerNode: HTMLDivElement | null = null; + tooltipNode: HTMLDivElement | null = null; + + static defaultProps: Partial = { + align: 'center', + anchor: 'center', + className: 'tooltip', + contentClassName: 'tooltip__content', + interactive: false, + offset: 0, + position: 'top', + stayOpen: false, + suppress: false, + wrapperClassName: 'tooltip__wrapper', + wrapText: false, + }; + + constructor(props: TooltipProps) { + super(props); + + this.state = { + isOpen: false, + wasTriggeredClose: false, + }; + } + + componentWillUnmount(): void { + this.removeScrollListener(); + } + + getCoordinates( + position: Position, + clearance: Clearance, + tooltipWidth: number, + tooltipHeight: number, + ): Pick { + const {align, offset = 0} = this.props; + let top = null; + + switch (position) { + case 'left': + return { + top: clearance.boundingRect.top + clearance.boundingRect.height / 2, + left: clearance.boundingRect.left - tooltipWidth + ARROW_SIZE + offset, + }; + case 'right': + return { + top: clearance.boundingRect.top + clearance.boundingRect.height / 2, + left: clearance.boundingRect.right + offset, + }; + case 'top': + top = clearance.boundingRect.top - tooltipHeight + ARROW_SIZE + offset; + break; + case 'bottom': + top = clearance.boundingRect.bottom + offset; + break; + default: + } + + switch (align) { + case 'start': + return { + top: top as number, + left: clearance.boundingRect.left, + }; + case 'center': + return { + top: top as number, + left: clearance.boundingRect.left + clearance.boundingRect.width / 2, + }; + case 'end': + return { + top: top as number, + left: clearance.boundingRect.left + clearance.boundingRect.width - tooltipWidth, + }; + default: + } + + return { + top: 0, + left: 0, + }; + } + + getIdealLocation( + anchor: Align, + position: Position, + ): { + anchor: Align; + position: Position; + coordinates: Pick; + } { + if (this.triggerNode == null || this.tooltipNode == null || anchor == null) { + return { + anchor, + position, + coordinates: {left: 0, top: 0}, + }; + } + + const clearance = getNodeClearance(this.triggerNode); + const tooltipRect = this.tooltipNode.getBoundingClientRect(); + const tooltipHeight = tooltipRect.height + ARROW_SIZE; + const tooltipWidth = tooltipRect.width + ARROW_SIZE; + + const newPosition = getPosition(position, clearance, tooltipWidth, tooltipHeight); + + return { + anchor: getAnchor( + newPosition !== 'left' && newPosition !== 'right', + anchor, + clearance, + tooltipWidth, + tooltipHeight, + ), + position: newPosition, + coordinates: this.getCoordinates(newPosition, clearance, tooltipWidth, tooltipHeight), + }; + } + + dismissTooltip = (forceClose?: boolean): void => { + const {stayOpen, onClose} = this.props; + const {isOpen} = this.state; + + if ((!stayOpen || forceClose) && isOpen) { + this.setState({isOpen: false}); + this.removeScrollListener(); + + if (onClose) { + onClose(); + } + } + }; + + handleTooltipMouseEnter = (): void => { + const {interactive} = this.props; + const {wasTriggeredClose} = this.state; + + if (interactive && !wasTriggeredClose) { + this.setState({isOpen: true}); + this.addScrollListener(); + } + }; + + handleTooltipMouseLeave = (): void => { + this.dismissTooltip(); + }; + + handleMouseEnter = (forceOpen?: boolean): void => { + const {props} = this; + + if (props.suppress && !forceOpen) { + return; + } + + if (props.anchor == null || props.position == null) { + return; + } + + const {anchor, position, coordinates} = this.getIdealLocation(props.anchor, props.position); + + this.setState({ + anchor, + isOpen: true, + position, + coordinates, + wasTriggeredClose: false, + }); + this.addScrollListener(); + + if (props.onOpen) { + props.onOpen(); + } + }; + + handleMouseLeave = (): void => { + this.dismissTooltip(); + + const {onMouseLeave} = this.props; + + if (onMouseLeave) { + onMouseLeave(); + } + }; + + isOpen = (): boolean => { + const {isOpen} = this.state; + + return isOpen; + }; + + addScrollListener(): void { + this.container.addEventListener('scroll', () => this.dismissTooltip()); + } + + removeScrollListener(): void { + if (this.container) { + this.container.removeEventListener('scroll', () => this.dismissTooltip()); + } + } + + render(): React.ReactNode { + const { + anchor: defaultAnchor, + position: defaultPosition, + children, + align, + className, + interactive, + wrapText, + width, + maxWidth, + wrapperClassName, + contentClassName, + content, + onClick, + } = this.props; + const {anchor: stateAnchor, position: statePosition, coordinates, isOpen} = this.state; + let tooltipStyle: React.CSSProperties = {}; + + // Get the anchor and position from state if possible. If not, get it from + // the props. + const anchor = stateAnchor || defaultAnchor; + const position = statePosition || defaultPosition; + + const tooltipClasses = classnames( + className, + `tooltip--anchor--${anchor}`, + `tooltip--position--${position}`, + `tooltip--align--${align}`, + { + 'is-interactive': interactive, + 'is-open': isOpen, + 'tooltip--no-wrap': !wrapText, + }, + ); + + if (coordinates) { + tooltipStyle = { + left: coordinates.left, + top: coordinates.top, + }; + } + + if (width) { + tooltipStyle.width = width; + } + + if (maxWidth) { + tooltipStyle.maxWidth = maxWidth; + } + + const appElement = document.getElementById('app'); + + if (appElement == null) { + return null; + } + + return ( +
    this.handleMouseEnter()} + onMouseLeave={() => this.handleMouseLeave()} + ref={(ref) => { + this.triggerNode = ref; + }}> + {children} + {ReactDOM.createPortal( +
    { + this.tooltipNode = ref; + }} + style={tooltipStyle} + onMouseEnter={this.handleTooltipMouseEnter} + onMouseLeave={this.handleTooltipMouseLeave}> +
    {content}
    +
    , + appElement, + )} +
    + ); + } +} + +export default Tooltip; diff --git a/client/src/javascript/components/general/WindowTitle.js b/client/src/javascript/components/general/WindowTitle.js deleted file mode 100644 index fc7121cf9..000000000 --- a/client/src/javascript/components/general/WindowTitle.js +++ /dev/null @@ -1,77 +0,0 @@ -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import connectStores from '../../util/connectStores'; -import {compute, getTranslationString} from '../../util/size'; -import EventTypes from '../../constants/EventTypes'; -import TransferDataStore from '../../stores/TransferDataStore'; - -const WindowTitle = props => { - const {intl, summary} = props; - - React.useEffect( - () => { - let title = 'Flood'; - - if (Object.keys(summary).length > 0) { - const down = compute(summary.downRate); - const up = compute(summary.upRate); - - const formattedDownSpeed = intl.formatNumber(down.value); - const formattedUpSpeed = intl.formatNumber(up.value); - - const translatedDownUnit = intl.formatMessage( - { - id: 'unit.speed', - defaultMessage: '{baseUnit}/s', - }, - { - baseUnit: intl.formatMessage({id: getTranslationString(down.unit)}), - }, - ); - const translatedUpUnit = intl.formatMessage( - { - id: 'unit.speed', - defaultMessage: '{baseUnit}/s', - }, - { - baseUnit: intl.formatMessage({id: getTranslationString(up.unit)}), - }, - ); - - title = intl.formatMessage( - { - id: 'window.title', - // \u2193 and \u2191 are down and up arrows, respectively - defaultMessage: '\u2193 {down} \u2191 {up} - Flood', - }, - { - down: `${formattedDownSpeed} ${translatedDownUnit}`, - up: `${formattedUpSpeed} ${translatedUpUnit}`, - }, - ); - } - - document.title = title; - }, - [intl, summary], - ); - - return null; -}; - -const ConnectedWindowTitle = connectStores(injectIntl(WindowTitle), () => { - return [ - { - store: TransferDataStore, - event: EventTypes.CLIENT_TRANSFER_SUMMARY_CHANGE, - getValue: ({store}) => { - return { - summary: store.getTransferSummary(), - }; - }, - }, - ]; -}); - -export default ConnectedWindowTitle; diff --git a/client/src/javascript/components/general/WindowTitle.tsx b/client/src/javascript/components/general/WindowTitle.tsx new file mode 100644 index 000000000..420ec28c7 --- /dev/null +++ b/client/src/javascript/components/general/WindowTitle.tsx @@ -0,0 +1,46 @@ +import {FC} from 'react'; +import {useIntl} from 'react-intl'; +import {observer} from 'mobx-react'; + +import {compute, getTranslationString} from '../../util/size'; +import TransferDataStore from '../../stores/TransferDataStore'; + +const WindowTitle: FC = observer(() => { + const {transferSummary: summary} = TransferDataStore; + const intl = useIntl(); + + let title = 'Flood'; + + if (summary != null && Object.keys(summary).length > 0) { + const down = compute(summary.downRate); + const up = compute(summary.upRate); + + const formattedDownSpeed = intl.formatNumber(down.value); + const formattedUpSpeed = intl.formatNumber(up.value); + + const translatedDownUnit = intl.formatMessage( + { + id: 'unit.speed', + }, + { + baseUnit: intl.formatMessage({id: getTranslationString(down.unit)}), + }, + ); + const translatedUpUnit = intl.formatMessage( + { + id: 'unit.speed', + }, + { + baseUnit: intl.formatMessage({id: getTranslationString(up.unit)}), + }, + ); + + title = `↓ ${formattedDownSpeed} ${translatedDownUnit} ↑ ${formattedUpSpeed} ${translatedUpUnit} - Flood`; + } + + document.title = title; + + return null; +}); + +export default WindowTitle; diff --git a/client/src/javascript/components/general/connection-settings/ClientConnectionSettingsForm.tsx b/client/src/javascript/components/general/connection-settings/ClientConnectionSettingsForm.tsx new file mode 100644 index 000000000..f8922480f --- /dev/null +++ b/client/src/javascript/components/general/connection-settings/ClientConnectionSettingsForm.tsx @@ -0,0 +1,96 @@ +import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; +import * as React from 'react'; + +import {SUPPORTED_CLIENTS} from '@shared/schema/constants/ClientConnectionSettings'; + +import type {ClientConnectionSettings} from '@shared/schema/ClientConnectionSettings'; + +import QBittorrentConnectionSettingsForm from './QBittorrentConnectionSettingsForm'; +import RTorrentConnectionSettingsForm from './RTorrentConnectionSettingsForm'; +import TransmissionConnectionSettingsForm from './TransmissionConnectionSettingsForm'; +import {FormRow, Select, SelectItem} from '../../../ui'; + +const DEFAULT_SELECTION: ClientConnectionSettings['client'] = 'rTorrent' as const; + +const getClientSelectItems = (): React.ReactNodeArray => + SUPPORTED_CLIENTS.map((client) => ( + + + + )); + +type ConnectionSettingsForm = + | QBittorrentConnectionSettingsForm + | RTorrentConnectionSettingsForm + | TransmissionConnectionSettingsForm; + +interface ClientConnectionSettingsFormStates { + client: ClientConnectionSettings['client']; +} + +class ClientConnectionSettingsForm extends React.Component { + settingsRef: React.RefObject = React.createRef(); + + constructor(props: WrappedComponentProps) { + super(props); + + this.state = { + client: DEFAULT_SELECTION, + }; + } + + getConnectionSettings(): ClientConnectionSettings | null { + const settingsForm = this.settingsRef as React.RefObject; + + if (settingsForm.current == null) { + return null; + } + + return settingsForm.current.getConnectionSettings(); + } + + render() { + const {intl} = this.props; + const {client} = this.state; + + let settingsForm: React.ReactNode = null; + switch (client) { + case 'qBittorrent': + settingsForm = ; + break; + case 'rTorrent': + settingsForm = ; + break; + case 'Transmission': + settingsForm = ; + break; + default: + break; + } + + return ( +
    + + + + {settingsForm} +
    + ); + } +} + +export type ClientConnectionSettingsFormType = ClientConnectionSettingsForm; + +export default injectIntl(ClientConnectionSettingsForm, {forwardRef: true}); diff --git a/client/src/javascript/components/general/connection-settings/QBittorrentConnectionSettingsForm.tsx b/client/src/javascript/components/general/connection-settings/QBittorrentConnectionSettingsForm.tsx new file mode 100644 index 000000000..7004e9e53 --- /dev/null +++ b/client/src/javascript/components/general/connection-settings/QBittorrentConnectionSettingsForm.tsx @@ -0,0 +1,110 @@ +import {FormattedMessage, IntlShape} from 'react-intl'; +import * as React from 'react'; + +import type {QBittorrentConnectionSettings} from '@shared/schema/ClientConnectionSettings'; + +import {FormGroup, FormRow, Textbox} from '../../../ui'; + +export interface QBittorrentConnectionSettingsProps { + intl: IntlShape; +} + +export interface QBittorrentConnectionSettingsFormData { + url: string; + username: string; + password: string; +} + +class QBittorrentConnectionSettingsForm extends React.Component< + QBittorrentConnectionSettingsProps, + QBittorrentConnectionSettingsFormData +> { + constructor(props: QBittorrentConnectionSettingsProps) { + super(props); + + this.state = { + url: '', + username: '', + password: '', + }; + } + + getConnectionSettings = (): QBittorrentConnectionSettings | null => { + if (this.state.url == null || this.state.url === '') { + return null; + } + + const settings: QBittorrentConnectionSettings = { + client: 'qBittorrent', + type: 'web', + version: 1, + url: this.state.url, + username: this.state.username || '', + password: this.state.password || '', + }; + + return settings; + }; + + handleFormChange = ( + event: React.MouseEvent | KeyboardEvent | React.ChangeEvent, + field: keyof QBittorrentConnectionSettingsFormData, + ): void => { + const inputElement = event.target as HTMLInputElement; + + if (inputElement == null) { + return; + } + + const {value} = inputElement; + + if (this.state[field] !== value) { + this.setState((prev) => ({ + ...prev, + [field]: value, + })); + } + }; + + render() { + return ( + + + + this.handleFormChange(e, 'url')} + id="url" + label={} + placeholder={this.props.intl.formatMessage({ + id: 'connection.settings.qbittorrent.url.input.placeholder', + })} + /> + + + this.handleFormChange(e, 'username')} + id="qbt-username" + label={} + placeholder={this.props.intl.formatMessage({ + id: 'connection.settings.qbittorrent.username.input.placeholder', + })} + autoComplete="off" + /> + this.handleFormChange(e, 'password')} + id="qbt-password" + label={} + placeholder={this.props.intl.formatMessage({ + id: 'connection.settings.qbittorrent.password.input.placeholder', + })} + autoComplete="off" + type="password" + /> + + + + ); + } +} + +export default QBittorrentConnectionSettingsForm; diff --git a/client/src/javascript/components/general/connection-settings/RTorrentConnectionSettingsForm.tsx b/client/src/javascript/components/general/connection-settings/RTorrentConnectionSettingsForm.tsx new file mode 100644 index 000000000..5cb291ece --- /dev/null +++ b/client/src/javascript/components/general/connection-settings/RTorrentConnectionSettingsForm.tsx @@ -0,0 +1,178 @@ +import {FormattedMessage, IntlShape} from 'react-intl'; +import * as React from 'react'; + +import type { + RTorrentConnectionSettings, + RTorrentSocketConnectionSettings, + RTorrentTCPConnectionSettings, +} from '@shared/schema/ClientConnectionSettings'; + +import {FormError, FormGroup, FormRow, FormRowGroup, Radio, Textbox} from '../../../ui'; + +export interface RTorrentConnectionSettingsProps { + intl: IntlShape; +} + +export interface RTorrentConnectionSettingsFormData { + type?: string; + socket?: string; + host?: string; + port?: string; +} + +class RTorrentConnectionSettingsForm extends React.Component< + RTorrentConnectionSettingsProps, + RTorrentConnectionSettingsFormData +> { + constructor(props: RTorrentConnectionSettingsProps) { + super(props); + this.state = { + type: 'socket', + }; + } + + getConnectionSettings = (): RTorrentConnectionSettings | null => { + const {type, socket, host, port} = this.state; + + switch (type) { + case 'socket': { + if (socket == null) { + return null; + } + const settings: RTorrentSocketConnectionSettings = { + client: 'rTorrent', + type: 'socket', + version: 1, + socket, + }; + return settings; + } + case 'tcp': { + const portAsNumber = Number(port); + if (host == null || portAsNumber == null) { + return null; + } + const settings: RTorrentTCPConnectionSettings = { + client: 'rTorrent', + type: 'tcp', + version: 1, + host, + port: portAsNumber, + }; + return settings; + } + default: + return null; + } + }; + + handleFormChange = ( + event: React.MouseEvent | KeyboardEvent | React.ChangeEvent, + field: keyof RTorrentConnectionSettingsFormData, + ) => { + const inputElement = event.target as HTMLInputElement; + + if (inputElement == null) { + return; + } + + const {value} = inputElement; + + if (this.state[field] !== value) { + this.setState((prev) => ({ + ...prev, + [field]: value, + })); + } + }; + + renderConnectionOptions() { + const {intl} = this.props; + const {type} = this.state; + + if (type === 'tcp') { + return ( + + + + {intl.formatMessage({ + id: 'connection.settings.rtorrent.type.tcp.warning', + })} + + + + this.handleFormChange(e, 'host')} + id="host" + label={} + placeholder={intl.formatMessage({ + id: 'connection.settings.rtorrent.host.input.placeholder', + })} + /> + this.handleFormChange(e, 'port')} + id="port" + label={} + placeholder={intl.formatMessage({ + id: 'connection.settings.rtorrent.port.input.placeholder', + })} + /> + + + ); + } + + return ( + + this.handleFormChange(e, 'socket')} + id="socket" + label={} + placeholder={intl.formatMessage({ + id: 'connection.settings.rtorrent.socket.input.placeholder', + })} + /> + + ); + } + + render() { + const {intl} = this.props; + const {type} = this.state; + + return ( + + + + + + this.handleFormChange(e, 'type')} + groupID="type" + id="socket" + grow={false} + defaultChecked={type === 'socket'}> + + + this.handleFormChange(e, 'type')} + groupID="type" + id="tcp" + grow={false} + defaultChecked={type === 'tcp'}> + + + + + + {this.renderConnectionOptions()} + + + ); + } +} + +export default RTorrentConnectionSettingsForm; diff --git a/client/src/javascript/components/general/connection-settings/TransmissionConnectionSettingsForm.tsx b/client/src/javascript/components/general/connection-settings/TransmissionConnectionSettingsForm.tsx new file mode 100644 index 000000000..54c331fc7 --- /dev/null +++ b/client/src/javascript/components/general/connection-settings/TransmissionConnectionSettingsForm.tsx @@ -0,0 +1,110 @@ +import {FormattedMessage, IntlShape} from 'react-intl'; +import * as React from 'react'; + +import type {TransmissionConnectionSettings} from '@shared/schema/ClientConnectionSettings'; + +import {FormGroup, FormRow, Textbox} from '../../../ui'; + +export interface TransmissionConnectionSettingsProps { + intl: IntlShape; +} + +export interface TransmissionConnectionSettingsFormData { + url: string; + username: string; + password: string; +} + +class TransmissionConnectionSettingsForm extends React.Component< + TransmissionConnectionSettingsProps, + TransmissionConnectionSettingsFormData +> { + constructor(props: TransmissionConnectionSettingsProps) { + super(props); + + this.state = { + url: '', + username: '', + password: '', + }; + } + + getConnectionSettings = (): TransmissionConnectionSettings | null => { + if (this.state.url == null || this.state.url === '') { + return null; + } + + const settings: TransmissionConnectionSettings = { + client: 'Transmission', + type: 'rpc', + version: 1, + url: this.state.url, + username: this.state.username || '', + password: this.state.password || '', + }; + + return settings; + }; + + handleFormChange = ( + event: React.MouseEvent | KeyboardEvent | React.ChangeEvent, + field: keyof TransmissionConnectionSettingsFormData, + ): void => { + const inputElement = event.target as HTMLInputElement; + + if (inputElement == null) { + return; + } + + const {value} = inputElement; + + if (this.state[field] !== value) { + this.setState((prev) => ({ + ...prev, + [field]: value, + })); + } + }; + + render() { + return ( + + + + this.handleFormChange(e, 'url')} + id="url" + label={} + placeholder={this.props.intl.formatMessage({ + id: 'connection.settings.transmission.url.input.placeholder', + })} + /> + + + this.handleFormChange(e, 'username')} + id="transmission-username" + label={} + placeholder={this.props.intl.formatMessage({ + id: 'connection.settings.transmission.username.input.placeholder', + })} + autoComplete="off" + /> + this.handleFormChange(e, 'password')} + id="transmission-password" + label={} + placeholder={this.props.intl.formatMessage({ + id: 'connection.settings.transmission.password.input.placeholder', + })} + autoComplete="off" + type="password" + /> + + + + ); + } +} + +export default TransmissionConnectionSettingsForm; diff --git a/client/src/javascript/components/general/filesystem/DirectoryFileList.js b/client/src/javascript/components/general/filesystem/DirectoryFileList.js deleted file mode 100644 index a0a1709e1..000000000 --- a/client/src/javascript/components/general/filesystem/DirectoryFileList.js +++ /dev/null @@ -1,123 +0,0 @@ -import {Checkbox} from 'flood-ui-kit'; -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import File from '../../icons/File'; -import PriorityMeter from './PriorityMeter'; -import Size from '../Size'; -import TorrentActions from '../../../actions/TorrentActions'; - -const ICONS = {file: }; -const METHODS_TO_BIND = ['handlePriorityChange']; - -class DirectoryFiles extends React.Component { - static propTypes = { - isParentSelected: PropTypes.bool, - path: PropTypes.array, - selectedItems: PropTypes.object, - }; - - static defaultProps = { - isParentSelected: false, - path: [], - selectedItems: {}, - }; - - constructor() { - super(); - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - getCurrentPath(file) { - return [...this.props.path, file.filename]; - } - - getIcon(file, isSelected) { - const changeHandler = (value, event) => { - this.handleFileSelect(file, isSelected, event); - }; - - return ( -
    -
    - -
    -
    - {ICONS.file} -
    -
    - ); - } - - handleFileSelect(file, isSelected, event) { - this.props.onItemSelect({ - ...file, - depth: this.props.depth, - event, - id: file.index, - isParentSelected: this.props.isParentSelected, - isSelected, - path: this.getCurrentPath(file), - type: 'file', - }); - } - - handlePriorityChange(fileIndex, priorityLevel) { - this.props.onPriorityChange(); - TorrentActions.setFilePriority(this.props.hash, [fileIndex], priorityLevel); - } - - render() { - const branch = Object.assign([], this.props.fileList); - - branch.sort((a, b) => a.filename.localeCompare(b.filename)); - - const files = branch.map(file => { - const isSelected = this.props.selectedItems[file.filename] && this.props.selectedItems[file.filename].isSelected; - const classes = classnames( - 'directory-tree__node file', - 'directory-tree__node--file directory-tree__node--selectable', - { - 'directory-tree__node--selected': isSelected, - }, - ); - - return ( -
    -
    - {this.getIcon(file, isSelected)} -
    {file.filename}
    -
    -
    - -
    -
    {file.percentComplete}%
    -
    - -
    -
    - ); - }); - - return
    {files}
    ; - } -} - -export default DirectoryFiles; diff --git a/client/src/javascript/components/general/filesystem/DirectoryFileList.tsx b/client/src/javascript/components/general/filesystem/DirectoryFileList.tsx new file mode 100644 index 000000000..f44c49857 --- /dev/null +++ b/client/src/javascript/components/general/filesystem/DirectoryFileList.tsx @@ -0,0 +1,129 @@ +import classnames from 'classnames'; +import * as React from 'react'; + +import type {TorrentContent, TorrentContentSelection, TorrentContentSelectionTree} from '@shared/types/TorrentContent'; +import type {TorrentProperties} from '@shared/types/Torrent'; + +import {Checkbox} from '../../../ui'; +import ConfigStore from '../../../stores/ConfigStore'; +import FileIcon from '../../icons/File'; +import PriorityMeter from '../PriorityMeter'; +import Size from '../Size'; +import TorrentActions from '../../../actions/TorrentActions'; + +interface DirectoryFilesProps { + depth: number; + hash: TorrentProperties['hash']; + items: TorrentContentSelectionTree['files']; + path: Array; + onItemSelect: (selection: TorrentContentSelection) => void; +} + +class DirectoryFiles extends React.Component { + static defaultProps = { + path: [], + items: {}, + }; + + getCurrentPath(file: TorrentContent) { + const {path} = this.props; + + return [...path, file.filename]; + } + + getIcon(file: TorrentContent, isSelected: boolean) { + return ( +
    +
    + this.handleFileSelect(file, isSelected)} /> +
    +
    + +
    +
    + ); + } + + handlePriorityChange = (fileIndex: React.ReactText, priorityLevel: number): void => { + const {hash} = this.props; + + TorrentActions.setFilePriority(hash, { + indices: [Number(fileIndex)], + priority: priorityLevel, + }); + }; + + handleFileSelect = (file: TorrentContent, isSelected: boolean): void => { + const {depth, onItemSelect} = this.props; + + onItemSelect({ + type: 'file', + depth, + path: this.getCurrentPath(file), + select: !isSelected, + }); + }; + + render() { + const {items, hash} = this.props; + + if (items == null) { + return null; + } + + const files = Object.values(items) + .sort((a, b) => a.filename.localeCompare(b.filename)) + .map((file) => { + const isSelected = (items && items[file.filename] && items[file.filename].isSelected) || false; + const classes = classnames( + 'directory-tree__node file', + 'directory-tree__node--file directory-tree__node--selectable', + { + 'directory-tree__node--selected': isSelected, + }, + ); + + return ( +
    +
    + {this.getIcon(file, isSelected)} +
    + {/* TODO: Add a WebAssembly decoding player if the feature is popular */} + + {file.filename} + +
    +
    +
    + +
    +
    {Math.trunc(file.percentComplete)}%
    +
    + +
    +
    + ); + }); + + return
    {files}
    ; + } +} + +export default DirectoryFiles; diff --git a/client/src/javascript/components/general/filesystem/DirectoryTree.js b/client/src/javascript/components/general/filesystem/DirectoryTree.js deleted file mode 100644 index 6c0670a86..000000000 --- a/client/src/javascript/components/general/filesystem/DirectoryTree.js +++ /dev/null @@ -1,102 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; - -import DirectoryFileList from './DirectoryFileList'; -// TODO: Fix this circular dependency -// eslint-disable-next-line import/no-cycle -import DirectoryTreeNode from './DirectoryTreeNode'; - -const METHODS_TO_BIND = ['getDirectoryTreeDomNodes']; - -class DirectoryTree extends React.Component { - static propTypes = { - isParentSelected: PropTypes.bool, - path: PropTypes.array, - selectedItems: PropTypes.object, - }; - - static defaultProps = { - isParentSelected: false, - path: [], - selectedItems: {}, - }; - - constructor() { - super(); - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - getDirectoryTreeDomNodes(tree = {}, depth = 0) { - const {files = []} = tree; - let {directories = {}} = tree; - const {hash} = this.props; - let fileList = null; - depth++; - - directories = Object.keys(directories) - .sort(this.sortDirectories) - .map((directoryName, index) => { - let subSelectedItems = {}; - - if (this.props.selectedItems.directories) { - subSelectedItems = this.props.selectedItems.directories[directoryName]; - } - - const subTree = directories[directoryName]; - const id = `${index}${depth}${directoryName}`; - const isSelected = subSelectedItems && subSelectedItems.isSelected; - - return ( - - ); - }); - - if (files.length) { - const subSelectedItems = this.props.selectedItems.files; - - fileList = ( - - ); - } - - return directories.concat([fileList]); - } - - sortDirectories(a, b) { - return a.localeCompare(b); - } - - render() { - return ( -
    {this.getDirectoryTreeDomNodes(this.props.tree, this.props.depth)}
    - ); - } -} - -export default DirectoryTree; diff --git a/client/src/javascript/components/general/filesystem/DirectoryTree.tsx b/client/src/javascript/components/general/filesystem/DirectoryTree.tsx new file mode 100644 index 000000000..a240479b1 --- /dev/null +++ b/client/src/javascript/components/general/filesystem/DirectoryTree.tsx @@ -0,0 +1,76 @@ +import {FC, ReactNode} from 'react'; + +import type {TorrentContentSelection, TorrentContentSelectionTree} from '@shared/types/TorrentContent'; +import type {TorrentProperties} from '@shared/types/Torrent'; + +import DirectoryFileList from './DirectoryFileList'; +// TODO: Fix this circular dependency +// eslint-disable-next-line import/no-cycle +import DirectoryTreeNode from './DirectoryTreeNode'; + +interface DirectoryTreeProps { + depth?: number; + path?: Array; + hash: TorrentProperties['hash']; + itemsTree: TorrentContentSelectionTree; + onItemSelect: (selection: TorrentContentSelection) => void; +} + +const DirectoryTree: FC = (props: DirectoryTreeProps) => { + const {depth = 0, itemsTree, hash, path, onItemSelect} = props; + const {files, directories} = itemsTree; + const childDepth = depth + 1; + + const directoryNodes: Array = + directories != null + ? Object.keys(directories) + .sort((a, b) => a.localeCompare(b)) + .map( + (directoryName, index): ReactNode => { + const subSelectedItems = itemsTree.directories && itemsTree.directories[directoryName]; + + const id = `${index}${childDepth}${directoryName}`; + const isSelected = (subSelectedItems && subSelectedItems.isSelected) || false; + + if (subSelectedItems == null) { + return null; + } + + return ( + + ); + }, + ) + : []; + + const fileList: ReactNode = + files != null && Object.keys(files).length > 0 ? ( + + ) : null; + + return
    {directoryNodes.concat(fileList)}
    ; +}; + +DirectoryTree.defaultProps = { + depth: 0, + path: [], +}; + +export default DirectoryTree; diff --git a/client/src/javascript/components/general/filesystem/DirectoryTreeNode.js b/client/src/javascript/components/general/filesystem/DirectoryTreeNode.js deleted file mode 100644 index 147eee1ba..000000000 --- a/client/src/javascript/components/general/filesystem/DirectoryTreeNode.js +++ /dev/null @@ -1,141 +0,0 @@ -import {Checkbox} from 'flood-ui-kit'; -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import FolderClosedSolid from '../../icons/FolderClosedSolid'; -import FolderOpenSolid from '../../icons/FolderOpenSolid'; -// TODO: Fix this circular dependency -// eslint-disable-next-line import/no-cycle -import DirectoryTree from './DirectoryTree'; - -const METHODS_TO_BIND = ['handleDirectoryClick', 'handleDirectorySelection']; - -class DirectoryTreeNode extends React.Component { - static propTypes = { - isParentSelected: PropTypes.bool, - path: PropTypes.array, - selectedItems: PropTypes.object, - }; - - static defaultProps = { - isParentSelected: false, - path: [], - selectedItems: {}, - }; - - constructor() { - super(); - - this.state = { - expanded: false, - }; - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - getCurrentPath() { - return [...this.props.path, this.props.directoryName]; - } - - getIcon() { - let icon = null; - - if (this.state.expanded) { - icon = ; - } else { - icon = ; - } - - return ( -
    -
    - -
    -
    - {icon} -
    -
    - ); - } - - getSubTree() { - if (this.state.expanded) { - return ( -
    - -
    - ); - } - - return null; - } - - handleDirectoryClick() { - this.setState(state => { - return { - expanded: !state.expanded, - }; - }); - } - - handleDirectorySelection(event) { - this.props.onItemSelect({ - depth: this.props.depth, - event, - id: this.props.id, - isParentSelected: this.props.isParentSelected, - isSelected: this.props.isSelected, - path: this.getCurrentPath(), - type: 'directory', - }); - } - - render() { - const branchClasses = classnames('directory-tree__branch', `directory-tree__branch--depth-${this.props.depth}`, { - 'directory-tree__node--selected': this.props.isSelected, - }); - const directoryClasses = classnames( - 'directory-tree__node', - 'directory-tree__node--selectable directory-tree__node--directory', - { - 'is-expanded': this.state.expanded, - }, - ); - - return ( -
    -
    -
    - {this.getIcon()} -
    {this.props.directoryName}
    -
    -
    - {this.getSubTree()} -
    - ); - } -} - -export default DirectoryTreeNode; diff --git a/client/src/javascript/components/general/filesystem/DirectoryTreeNode.tsx b/client/src/javascript/components/general/filesystem/DirectoryTreeNode.tsx new file mode 100644 index 000000000..1eb99536e --- /dev/null +++ b/client/src/javascript/components/general/filesystem/DirectoryTreeNode.tsx @@ -0,0 +1,144 @@ +import classnames from 'classnames'; +import {Component} from 'react'; + +import type {TorrentContentSelection, TorrentContentSelectionTree} from '@shared/types/TorrentContent'; +import type {TorrentProperties} from '@shared/types/Torrent'; + +import {Checkbox} from '../../../ui'; +import FolderClosedSolid from '../../icons/FolderClosedSolid'; +import FolderOpenSolid from '../../icons/FolderOpenSolid'; +// TODO: Fix this circular dependency +// eslint-disable-next-line import/no-cycle +import DirectoryTree from './DirectoryTree'; + +interface DirectoryTreeNodeProps { + id: string; + depth: number; + hash: TorrentProperties['hash']; + path: Array; + directoryName: string; + itemsTree: TorrentContentSelectionTree; + isSelected: boolean; + onItemSelect: (selection: TorrentContentSelection) => void; +} + +interface DirectoryTreeNodeStates { + expanded: boolean; +} + +class DirectoryTreeNode extends Component { + static defaultProps = { + path: [], + selectedItems: {}, + }; + + constructor(props: DirectoryTreeNodeProps) { + super(props); + + this.state = { + expanded: false, + }; + } + + getCurrentPath() { + const {path, directoryName} = this.props; + + return [...path, directoryName]; + } + + getIcon() { + const {id, isSelected} = this.props; + const {expanded} = this.state; + + let icon = null; + if (expanded) { + icon = ; + } else { + icon = ; + } + + return ( +
    +
    + +
    +
    + {icon} +
    +
    + ); + } + + getSubTree() { + const {depth, itemsTree, hash, onItemSelect} = this.props; + const {expanded} = this.state; + + if (expanded) { + return ( +
    + +
    + ); + } + + return null; + } + + handleDirectoryClick = (): void => { + this.setState((state) => ({ + expanded: !state.expanded, + })); + }; + + handleDirectorySelection = (): void => { + const {depth, isSelected, onItemSelect} = this.props; + + onItemSelect({ + type: 'directory', + depth, + path: this.getCurrentPath(), + select: !isSelected, + }); + }; + + render() { + const {depth, directoryName, isSelected} = this.props; + const {expanded} = this.state; + + const branchClasses = classnames('directory-tree__branch', `directory-tree__branch--depth-${depth}`, { + 'directory-tree__node--selected': isSelected, + }); + const directoryClasses = classnames( + 'directory-tree__node', + 'directory-tree__node--selectable directory-tree__node--directory', + { + 'is-expanded': expanded, + }, + ); + + return ( +
    +
    +
    + {this.getIcon()} +
    {directoryName}
    +
    +
    + {this.getSubTree()} +
    + ); + } +} + +export default DirectoryTreeNode; diff --git a/client/src/javascript/components/general/filesystem/FilesystemBrowser.js b/client/src/javascript/components/general/filesystem/FilesystemBrowser.js deleted file mode 100644 index 58f3cd0e8..000000000 --- a/client/src/javascript/components/general/filesystem/FilesystemBrowser.js +++ /dev/null @@ -1,190 +0,0 @@ -import React from 'react'; -import {defineMessages} from 'react-intl'; - -import ArrowIcon from '../../icons/ArrowIcon'; -import CustomScrollbars from '../CustomScrollbars'; -import File from '../../icons/File'; -import FolderClosedSolid from '../../icons/FolderClosedSolid'; -import FloodActions from '../../../actions/FloodActions'; - -const MESSAGES = defineMessages({ - EACCES: { - id: 'filesystem.error.eacces', - defaultMessage: 'Flood does not have permission to read this directory.', - }, - ENOENT: { - id: 'filesystem.error.enoent', - defaultMessage: 'This path does not exist. It will be created.', - }, - emptyDirectory: { - id: 'filesystem.empty.directory', - defaultMessage: 'Empty directory.', - }, - fetching: { - id: 'filesystem.fetching', - defaultMessage: 'Fetching directory structure...', - }, -}); - -class FilesystemBrowser extends React.PureComponent { - constructor(props) { - super(props); - - this.state = { - errorResponse: null, - separator: '/', - }; - } - - componentDidMount() { - this.fetchDirectoryListForCurrentDirectory(); - } - - componentDidUpdate(prevProps) { - if (prevProps.directory !== this.props.directory) { - this.fetchDirectoryListForCurrentDirectory(); - } - } - - fetchDirectoryListForCurrentDirectory = () => { - FloodActions.fetchDirectoryList({path: this.props.directory}) - .then(response => { - this.setState({ - ...response, - errorResponse: null, - }); - }) - .catch(error => { - this.setState({errorResponse: error.response}); - }); - }; - - getNewDestination(nextDirectorySegment) { - const {separator} = this.state; - const {directory} = this.props; - - if (directory.endsWith(separator)) { - return `${directory}${nextDirectorySegment}`; - } - - return `${directory}${separator}${nextDirectorySegment}`; - } - - handleDirectoryClick = directory => { - const nextDirectory = this.getNewDestination(directory); - - if (this.props.onDirectorySelection) { - this.props.onDirectorySelection(nextDirectory); - } - }; - - handleParentDirectoryClick = () => { - const {separator} = this.state; - let {directory} = this.props; - - if (directory.endsWith(separator)) { - directory = directory.substring(0, directory.length - 1); - } - - const directoryArr = directory.split(separator); - directoryArr.pop(); - - directory = directoryArr.join(separator); - - if (this.props.onDirectorySelection) { - this.props.onDirectorySelection(directory); - } - }; - - render() { - const {directories, errorResponse, files = [], hasParent} = this.state; - let errorMessage = null; - let listItems = null; - let parentDirectory = null; - let shouldShowDirectoryList = true; - let shouldForceShowParentDirectory = false; - - if (directories == null) { - shouldShowDirectoryList = false; - errorMessage = ( -
    - {this.props.intl.formatMessage(MESSAGES.fetching)} -
    - ); - } - - if (errorResponse && errorResponse.data && errorResponse.data.code && MESSAGES[errorResponse.data.code]) { - shouldShowDirectoryList = false; - - if (errorResponse.data.code === 'EACCES') { - shouldForceShowParentDirectory = true; - } - - errorMessage = ( -
    - {this.props.intl.formatMessage(MESSAGES[errorResponse.data.code])} -
    - ); - } - - if (hasParent || shouldForceShowParentDirectory) { - parentDirectory = ( -
  • - - {this.props.intl.formatMessage({ - id: 'filesystem.parent.directory', - defaultMessage: 'Parent Directory', - })} -
  • - ); - } - - if (shouldShowDirectoryList) { - const directoryList = directories.map((directory, index) => ( -
  • this.handleDirectoryClick(directory)}> - - {directory} -
  • - )); - - const filesList = files.map((file, index) => ( - // TODO: Find a better key - // eslint-disable-next-line react/no-array-index-key -
  • - - {file} -
  • - )); - - listItems = directoryList.concat(filesList); - } - - if ((!listItems || listItems.length === 0) && !errorMessage) { - errorMessage = ( -
    - {this.props.intl.formatMessage(MESSAGES.emptyDirectory)} -
    - ); - } - - return ( - -
    - {parentDirectory} - {errorMessage} - {listItems} -
    -
    - ); - } -} - -export default FilesystemBrowser; diff --git a/client/src/javascript/components/general/filesystem/FilesystemBrowser.tsx b/client/src/javascript/components/general/filesystem/FilesystemBrowser.tsx new file mode 100644 index 000000000..ac9e8d009 --- /dev/null +++ b/client/src/javascript/components/general/filesystem/FilesystemBrowser.tsx @@ -0,0 +1,208 @@ +import {defineMessages, WrappedComponentProps} from 'react-intl'; +import * as React from 'react'; + +import ArrowIcon from '../../icons/ArrowIcon'; +import File from '../../icons/File'; +import FolderClosedSolid from '../../icons/FolderClosedSolid'; +import FloodActions from '../../../actions/FloodActions'; + +const MESSAGES = defineMessages({ + EACCES: { + id: 'filesystem.error.eacces', + }, + ENOENT: { + id: 'filesystem.error.enoent', + }, + emptyDirectory: { + id: 'filesystem.empty.directory', + }, + fetching: { + id: 'filesystem.fetching', + }, + unknownError: { + id: 'filesystem.error.unknown', + }, +}); + +interface FilesystemBrowserProps extends WrappedComponentProps { + selectable?: 'files' | 'directories'; + directory: string; + onItemSelection?: (newDestination: string, isDirectory?: boolean) => void; +} + +interface FilesystemBrowserStates { + errorResponse: {data?: NodeJS.ErrnoException} | null; + separator: string; + directories?: Array; + files?: Array; +} + +class FilesystemBrowser extends React.PureComponent { + constructor(props: FilesystemBrowserProps) { + super(props); + + this.state = { + errorResponse: null, + separator: '/', + }; + } + + componentDidMount(): void { + this.fetchDirectoryListForCurrentDirectory(); + } + + componentDidUpdate(prevProps: FilesystemBrowserProps): void { + const {directory} = this.props; + + if (prevProps.directory !== directory) { + this.fetchDirectoryListForCurrentDirectory(); + } + } + + getNewDestination(nextDirectorySegment: string): string { + const {separator} = this.state; + const {directory} = this.props; + + if (directory?.endsWith(separator)) { + return `${directory}${nextDirectorySegment}`; + } + + return `${directory}${separator}${nextDirectorySegment}`; + } + + fetchDirectoryListForCurrentDirectory = (): void => { + const {directory} = this.props; + + FloodActions.fetchDirectoryList({path: directory}) + .then((response) => { + this.setState({ + ...response, + errorResponse: null, + }); + }) + .catch((error) => { + this.setState({errorResponse: error.response}); + }); + }; + + handleItemClick = (item: string, isDirectory = true) => { + if (this.props.onItemSelection) { + this.props.onItemSelection(this.getNewDestination(item), isDirectory); + } + }; + + handleParentDirectoryClick = () => { + const {separator} = this.state; + let {directory} = this.props; + + if (directory.endsWith(separator)) { + directory = directory.substring(0, directory.length - 1); + } + + const directoryArr = directory.split(separator); + directoryArr.pop(); + + directory = directoryArr.join(separator); + + if (this.props.onItemSelection) { + this.props.onItemSelection(directory); + } + }; + + render() { + const {intl, selectable} = this.props; + const {directories, errorResponse, files} = this.state; + let errorMessage = null; + let listItems = null; + let parentDirectory = null; + let shouldShowDirectoryList = true; + + if ((directories == null && selectable === 'directories') || (files == null && selectable === 'files')) { + shouldShowDirectoryList = false; + errorMessage = ( +
    + {intl.formatMessage(MESSAGES.fetching)} +
    + ); + } + + if (errorResponse && errorResponse.data && errorResponse.data.code) { + shouldShowDirectoryList = false; + + const messageConfig = MESSAGES[errorResponse.data.code as keyof typeof MESSAGES] || MESSAGES.unknownError; + + errorMessage = ( +
    + {intl.formatMessage(messageConfig)} +
    + ); + } + + parentDirectory = ( +
  • + + {intl.formatMessage({ + id: 'filesystem.parent.directory', + })} +
  • + ); + + if (shouldShowDirectoryList) { + const directoryList: React.ReactNodeArray = + directories != null + ? directories.map((directory, index) => ( +
  • this.handleItemClick(directory) : undefined}> + + {directory} +
  • + )) + : []; + + const filesList: React.ReactNodeArray = + files != null + ? files.map((file, index) => ( +
  • this.handleItemClick(file, false) : undefined}> + + {file} +
  • + )) + : []; + + listItems = directoryList.concat(filesList); + } + + if ((!listItems || listItems.length === 0) && !errorMessage) { + errorMessage = ( +
    + {intl.formatMessage(MESSAGES.emptyDirectory)} +
    + ); + } + + return ( +
    + {parentDirectory} + {errorMessage} + {listItems} +
    + ); + } +} + +export default FilesystemBrowser; diff --git a/client/src/javascript/components/general/filesystem/PriorityMeter.js b/client/src/javascript/components/general/filesystem/PriorityMeter.js deleted file mode 100644 index 307b70709..000000000 --- a/client/src/javascript/components/general/filesystem/PriorityMeter.js +++ /dev/null @@ -1,103 +0,0 @@ -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import PiorityLevels from '../../../constants/PriorityLevels'; - -const METHODS_TO_BIND = ['handleClick']; - -class PriorityMeter extends React.Component { - constructor() { - super(); - - this.state = { - optimisticData: { - level: null, - }, - }; - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - componentDidMount() { - if (this.props.bindExternalChangeHandler) { - this.props.bindExternalChangeHandler(this.handleClick); - } - } - - componentWillUnmount() { - if (this.props.bindExternalChangeHandler) { - this.props.bindExternalChangeHandler(null); - } - } - - getPriorityLabel() { - switch (PiorityLevels[this.props.priorityType][this.getPriorityLevel()]) { - case 'DONT_DOWNLOAD': - return this.props.intl.formatMessage({ - id: 'priority.dont.download', - defaultMessage: "Don't Download", - }); - case 'HIGH': - return this.props.intl.formatMessage({ - id: 'priority.high', - defaultMessage: 'High', - }); - case 'LOW': - return this.props.intl.formatMessage({ - id: 'priority.low', - defaultMessage: 'Low', - }); - case 'NORMAL': - return this.props.intl.formatMessage({ - id: 'priority.normal', - defaultMessage: 'Normal', - }); - default: - return ''; - } - } - - getPriorityLevel() { - if (this.state.optimisticData.level != null) { - return this.state.optimisticData.level; - } - - return this.props.level; - } - - handleClick() { - let level = this.getPriorityLevel(); - - if (level++ >= this.props.maxLevel) { - level = 0; - } - - this.setState({optimisticData: {level}}); - this.props.onChange(this.props.id, level); - } - - render() { - let label = null; - - if (this.props.showLabel) { - label = {this.getPriorityLabel()}; - } - - return ( -
    -
    - {label} -
    - ); - } -} - -export default injectIntl(PriorityMeter); diff --git a/client/src/javascript/components/general/filesystem/TorrentDestination.js b/client/src/javascript/components/general/filesystem/TorrentDestination.js deleted file mode 100644 index 48d49f481..000000000 --- a/client/src/javascript/components/general/filesystem/TorrentDestination.js +++ /dev/null @@ -1,182 +0,0 @@ -import _ from 'lodash'; -import {Checkbox, ContextMenu, FormElementAddon, FormRow, FormRowGroup, Portal, Textbox} from 'flood-ui-kit'; -import {FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import EventTypes from '../../../constants/EventTypes'; -import FilesystemBrowser from './FilesystemBrowser'; -import Search from '../../icons/Search'; -import SettingsStore from '../../../stores/SettingsStore'; -import UIStore from '../../../stores/UIStore'; - -class TorrentDestination extends React.Component { - contextMenuInstanceRef = null; - - contextMenuNodeRef = null; - - textboxRef = null; - - constructor(props) { - super(props); - - const destination = - props.suggested || - SettingsStore.getFloodSettings('torrentDestination') || - SettingsStore.getClientSettings('directoryDefault') || - ''; - - this.state = { - destination, - isDirectoryListOpen: false, - }; - } - - componentDidMount() { - UIStore.listen(EventTypes.UI_MODAL_DISMISSED, this.handleModalDismiss); - // TODO: Fix ContextMenu in flood-ui-kit and remove the forced double render - // https://github.com/jfurrow/flood-ui-kit/issues/6 - this.forceUpdate(); - } - - componentDidUpdate(_prevProps, prevState) { - if (!prevState.isDirectoryListOpen && this.state.isDirectoryListOpen) { - this.addDestinationOpenEventListeners(); - } else if (prevState.isDirectoryListOpen && !this.state.isDirectoryListOpen) { - this.removeDestinationOpenEventListeners(); - } - } - - componentWillUnmount() { - UIStore.unlisten(EventTypes.UI_MODAL_DISMISSED, this.handleModalDismiss); - this.removeDestinationOpenEventListeners(); - } - - addDestinationOpenEventListeners() { - global.document.addEventListener('click', this.handleDocumentClick); - global.addEventListener('resize', this.handleWindowResize); - } - - closeDirectoryList = () => { - if (this.state.isDirectoryListOpen) { - this.setState({isDirectoryListOpen: false}); - } - }; - - /* eslint-disable react/sort-comp */ - handleDestinationInputChange = _.debounce( - () => { - const destination = this.textboxRef.value; - - if (this.props.onChange) { - this.props.onChange(destination); - } - - this.setState({destination}); - }, - 100, - {leading: true}, - ); - /* eslint-enable react/sort-comp */ - - handleDirectoryListButtonClick = () => { - this.setState(state => { - const isOpening = !state.isDirectoryListOpen; - - return { - isDirectoryListOpen: isOpening, - }; - }); - }; - - handleDirectorySelection = destination => { - this.textboxRef.value = destination; - this.setState({destination}); - }; - - handleDocumentClick = () => { - this.closeDirectoryList(); - }; - - handleModalDismiss = () => { - this.closeDirectoryList(); - }; - - handleWindowResize = () => { - this.closeDirectoryList(); - }; - - removeDestinationOpenEventListeners() { - global.document.removeEventListener('click', this.handleDocumentClick); - global.removeEventListener('resize', this.handleWindowResize); - } - - toggleOpenState = () => { - this.setState(state => { - return { - isDirectoryListOpen: !state.isDirectoryListOpen, - }; - }); - }; - - render() { - const {destination, isDirectoryListOpen} = this.state; - - return ( - - - event.nativeEvent.stopImmediatePropagation()} - placeholder={this.props.intl.formatMessage({ - id: 'torrents.add.destination.placeholder', - defaultMessage: 'Destination', - })} - setRef={ref => { - this.textboxRef = ref; - }}> - - - - - event.nativeEvent.stopImmediatePropagation()} - overlayProps={{isInteractive: false}} - padding={false} - ref={ref => { - this.contextMenuInstanceRef = ref; - }} - setRef={ref => { - this.contextMenuNodeRef = ref; - }} - scrolling={false} - triggerRef={this.textboxRef}> - - - - - - - - - - - - ); - } -} - -export default injectIntl(TorrentDestination, {withRef: true}); diff --git a/client/src/javascript/components/general/form-elements/Dropdown.js b/client/src/javascript/components/general/form-elements/Dropdown.js deleted file mode 100644 index 46d1a6a88..000000000 --- a/client/src/javascript/components/general/form-elements/Dropdown.js +++ /dev/null @@ -1,201 +0,0 @@ -import _ from 'lodash'; -import classnames from 'classnames'; -import {CSSTransition, TransitionGroup} from 'react-transition-group'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import EventTypes from '../../../constants/EventTypes'; -import UIActions from '../../../actions/UIActions'; -import UIStore from '../../../stores/UIStore'; - -const METHODS_TO_BIND = [ - 'closeDropdown', - 'openDropdown', - 'getDropdownButton', - 'getDropdownMenu', - 'getDropdownMenuItems', - 'handleActiveDropdownChange', - 'handleDropdownClick', - 'handleItemSelect', - 'handleKeyPress', -]; - -class Dropdown extends React.Component { - static propTypes = { - direction: PropTypes.oneOf(['down', 'up']), - header: PropTypes.node, - trigger: PropTypes.node, - matchButtonWidth: PropTypes.bool, - menuItems: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.object)).isRequired, - noWrap: PropTypes.bool, - onOpen: PropTypes.func, - width: PropTypes.oneOf(['small', 'medium', 'large']), - }; - - static defaultProps = { - baseClassName: 'dropdown', - direction: 'down', - dropdownWrapperClass: 'dropdown', - dropdownButtonClass: 'dropdown__trigger', - matchButtonWidth: false, - noWrap: false, - }; - - constructor() { - super(); - - this.id = _.uniqueId('dropdown_'); - - this.state = { - isOpen: false, - }; - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - - this.handleKeyPress = _.throttle(this.handleKeyPress, 200); - } - - closeDropdown() { - global.removeEventListener('keydown', this.handleKeyPress); - global.removeEventListener('click', this.closeDropdown); - UIStore.unlisten(EventTypes.UI_DROPDOWN_MENU_CHANGE, this.handleActiveDropdownChange); - - this.setState({isOpen: false}); - } - - openDropdown() { - global.addEventListener('keydown', this.handleKeyPress); - global.addEventListener('click', this.closeDropdown); - UIStore.listen(EventTypes.UI_DROPDOWN_MENU_CHANGE, this.handleActiveDropdownChange); - - this.setState({isOpen: true}); - - if (this.props.onOpen) { - this.props.onOpen(); - } - - UIActions.displayDropdownMenu(this.id); - } - - handleDropdownClick(event) { - event.stopPropagation(); - - if (this.state.isOpen) { - this.closeDropdown(); - } else { - this.openDropdown(); - } - } - - handleActiveDropdownChange() { - if (this.state.isOpen && UIStore.getActiveDropdownMenu() !== this.id) { - this.closeDropdown(); - } - } - - handleItemSelect(item) { - this.closeDropdown(); - this.props.handleItemSelect(item); - } - - handleKeyPress(event) { - if (this.state.isOpen && event.keyCode === 27) { - this.closeDropdown(); - } - } - - getDropdownButton(options = {}) { - let label = this.props.header; - - if (options.trigger && !!this.props.trigger) { - label = this.props.trigger; - } - - return ( -
    - {label} -
    - ); - } - - getDropdownMenu(items) { - // TODO: Rewrite this function, wtf was I thinking - const arrayMethod = this.props.direction === 'up' ? 'unshift' : 'push'; - const content = [ -
    - {this.getDropdownButton({header: true, trigger: false})} -
    , - ]; - const dropdownLists = items.map((itemList, index) => ( - // TODO: Find a better key - // eslint-disable-next-line react/no-array-index-key -
    - {this.getDropdownMenuItems(itemList)} -
    - )); - - content[arrayMethod]( -
      - {dropdownLists} -
    , - ); - - return ( - -
    {content}
    -
    - ); - } - - getDropdownMenuItems(listItems) { - return listItems.map((property, index) => { - const classes = classnames('dropdown__item menu__item', property.className, { - 'is-selectable': property.selectable !== false, - 'is-selected': property.selected, - }); - let clickHandler = null; - - if (property.selectable !== false) { - clickHandler = this.handleItemSelect.bind(this, property); - } - - return ( - // TODO: Find a better key - // eslint-disable-next-line react/no-array-index-key -
  • - {property.displayName} -
  • - ); - }); - } - - render() { - const dropdownWrapperClass = classnames( - this.props.dropdownWrapperClass, - `${this.props.baseClassName}--direction-${this.props.direction}`, - { - [`${this.props.baseClassName}--match-button-width`]: this.props.matchButtonWidth, - [`${this.props.baseClassName}--width-${this.props.width}`]: this.props.width != null, - [`${this.props.baseClassName}--no-wrap`]: this.props.nowrap, - 'is-expanded': this.state.isOpen, - }, - ); - - let menu = null; - - if (this.state.isOpen) { - menu = this.getDropdownMenu(this.props.menuItems); - } - - return ( -
    - {this.getDropdownButton({header: false, trigger: true})} - {menu} -
    - ); - } -} - -export default Dropdown; diff --git a/client/src/javascript/components/general/form-elements/Dropdown.tsx b/client/src/javascript/components/general/form-elements/Dropdown.tsx new file mode 100644 index 000000000..bbea94815 --- /dev/null +++ b/client/src/javascript/components/general/form-elements/Dropdown.tsx @@ -0,0 +1,174 @@ +import classnames from 'classnames'; +import {CSSTransition, TransitionGroup} from 'react-transition-group'; +import {FC, MouseEvent, ReactNode, useCallback, useRef} from 'react'; +import {observer} from 'mobx-react'; +import uniqueId from 'lodash/uniqueId'; +import {useKeyPressEvent} from 'react-use'; + +import UIActions from '../../../actions/UIActions'; +import UIStore from '../../../stores/UIStore'; + +interface DropdownButtonProps { + className?: string; + label: ReactNode; + onClick: (event: MouseEvent) => void; +} + +const DropdownButton: FC = ({className, label, onClick}: DropdownButtonProps) => ( +
    + {label} +
    +); + +DropdownButton.defaultProps = { + className: undefined, +}; + +export interface DropdownItem { + className?: string; + displayName: ReactNode; + selectable?: boolean; + selected?: boolean; + property?: T; + value?: number | null; +} + +type DropdownItems = Array>; + +interface DropdownProps { + header: ReactNode; + trigger?: ReactNode; + dropdownButtonClass?: string; + menuItems: Array>; + handleItemSelect: (item: DropdownItem) => void; + onOpen?: () => void; + + dropdownWrapperClass?: string; + baseClassName?: string; + direction?: 'down' | 'up'; + width?: 'small' | 'medium' | 'large'; + matchButtonWidth?: boolean; + noWrap?: boolean; +} + +const Dropdown = observer( + ({ + baseClassName, + dropdownWrapperClass, + dropdownButtonClass, + direction, + header, + matchButtonWidth, + menuItems, + noWrap, + trigger, + width, + handleItemSelect, + onOpen, + }: DropdownProps) => { + const id = useRef(uniqueId('dropdown_')); + const isOpen = UIStore.activeDropdownMenu === id.current; + const dropdownWrapperClassName = classnames(dropdownWrapperClass, `${baseClassName}--direction-${direction}`, { + [`${baseClassName}--match-button-width`]: matchButtonWidth, + [`${baseClassName}--width-${width}`]: width != null, + [`${baseClassName}--no-wrap`]: noWrap, + 'is-expanded': isOpen, + }); + + const closeDropdown = useCallback(() => { + window.removeEventListener('click', closeDropdown); + + UIActions.displayDropdownMenu(null); + }, []); + + useKeyPressEvent('Escape', () => closeDropdown()); + + const openDropdown = useCallback(() => { + window.addEventListener('click', closeDropdown); + + if (onOpen) { + onOpen(); + } + + UIActions.displayDropdownMenu(id.current); + }, [closeDropdown, onOpen]); + + const handleDropdownClick = (event: MouseEvent): void => { + event.stopPropagation(); + + if (isOpen) { + closeDropdown(); + } else { + openDropdown(); + } + }; + + let contentElement: ReactNode; + if (isOpen) { + const headerElement = ( +
    + +
    + ); + + const listElement = ( +
      + {menuItems.map((items, index) => ( + // eslint-disable-next-line react/no-array-index-key +
      + {items.map((item, itemIndex) => { + const classes = classnames('dropdown__item menu__item', item.className, { + 'is-selectable': item.selectable !== false, + 'is-selected': item.selected, + }); + + return ( +
    • { + closeDropdown(); + handleItemSelect(item); + } + }> + {item.displayName} +
    • + ); + })} +
      + ))} +
    + ); + + contentElement = ( + +
    + {direction === 'up' ? [listElement, headerElement] : [headerElement, listElement]} +
    +
    + ); + } + + return ( +
    + + {contentElement} +
    + ); + }, +); + +(Dropdown as FC).defaultProps = { + baseClassName: 'dropdown', + direction: 'down', + dropdownWrapperClass: 'dropdown', + dropdownButtonClass: 'dropdown__trigger', + matchButtonWidth: false, + noWrap: false, +}; + +export default Dropdown; diff --git a/client/src/javascript/components/general/form-elements/FileDropzone.tsx b/client/src/javascript/components/general/form-elements/FileDropzone.tsx new file mode 100644 index 000000000..c0a6c222e --- /dev/null +++ b/client/src/javascript/components/general/form-elements/FileDropzone.tsx @@ -0,0 +1,94 @@ +import Dropzone from 'react-dropzone'; +import {FormattedMessage} from 'react-intl'; +import {FC, useEffect, useState} from 'react'; + +import CloseIcon from '../../icons/Close'; +import FileIcon from '../../icons/File'; +import FilesIcon from '../../icons/Files'; +import {FormRowItem} from '../../../ui'; + +export type ProcessedFiles = Array<{name: string; data: string}>; + +interface FileDropzoneProps { + onFilesChanged: (files: ProcessedFiles) => void; +} + +const FileDropzone: FC = ({onFilesChanged}: FileDropzoneProps) => { + const [files, setFiles] = useState([]); + + useEffect(() => { + onFilesChanged(files); + }, [files, onFilesChanged]); + + return ( + + + {files.length > 0 ? ( +
      { + event.stopPropagation(); + }}> + {files.map((file, index) => ( +
    • + + + + {file.name} + { + const newArray = files.slice(); + newArray.splice(index, 1); + setFiles(newArray); + }}> + + +
    • + ))} +
    + ) : null} + ) => { + const processedFiles: ProcessedFiles = []; + addedFiles.forEach((file) => { + const reader = new FileReader(); + reader.onload = (e) => { + if (e.target?.result != null && typeof e.target.result === 'string') { + processedFiles.push({ + name: file.name, + data: e.target.result.split('base64,')[1], + }); + } + if (processedFiles.length === addedFiles.length) { + setFiles(files.concat(processedFiles)); + } + }; + reader.readAsDataURL(file); + }); + }}> + {({getRootProps, getInputProps, isDragActive}) => ( +
    + +
    +
    + +
    + {' '} + + + + . +
    +
    + )} +
    +
    + ); +}; + +export default FileDropzone; diff --git a/client/src/javascript/components/general/form-elements/FilesystemBrowserTextbox.tsx b/client/src/javascript/components/general/form-elements/FilesystemBrowserTextbox.tsx new file mode 100644 index 000000000..9c7e2dfb1 --- /dev/null +++ b/client/src/javascript/components/general/form-elements/FilesystemBrowserTextbox.tsx @@ -0,0 +1,162 @@ +import debounce from 'lodash/debounce'; +import {FormattedMessage, useIntl} from 'react-intl'; +import {forwardRef, MutableRefObject, ReactNode, useEffect, useRef, useState} from 'react'; +import {useEnsuredForwardedRef} from 'react-use'; + +import {Checkbox, ContextMenu, FormElementAddon, FormRow, FormRowGroup, Portal, Textbox} from '../../../ui'; +import FilesystemBrowser from '../filesystem/FilesystemBrowser'; +import Search from '../../icons/Search'; +import SettingStore from '../../../stores/SettingStore'; + +interface FilesystemBrowserTextboxProps { + id: string; + label?: ReactNode; + selectable?: 'directories' | 'files'; + suggested?: string; + showBasePathToggle?: boolean; + showCompletedToggle?: boolean; + onChange?: (destination: string) => void; +} + +const FilesystemBrowserTextbox = forwardRef( + ( + { + id, + label, + selectable, + suggested, + showBasePathToggle, + showCompletedToggle, + onChange, + }: FilesystemBrowserTextboxProps, + ref, + ) => { + const [destination, setDestination] = useState( + suggested ?? + SettingStore.floodSettings.torrentDestinations?.[''] ?? + SettingStore.clientSettings?.directoryDefault ?? + '', + ); + const [isDirectoryListOpen, setIsDirectoryListOpen] = useState(false); + + const formRowRef = useRef(null); + const textboxRef = useEnsuredForwardedRef(ref as MutableRefObject); + + const intl = useIntl(); + + useEffect(() => { + const closeDirectoryList = (): void => { + setIsDirectoryListOpen(false); + }; + + const handleDocumentClick = (e: Event): void => { + if (!formRowRef.current?.contains((e.target as unknown) as Node)) { + closeDirectoryList(); + } + }; + + document.addEventListener('click', handleDocumentClick); + window.addEventListener('resize', closeDirectoryList); + + return () => { + document.removeEventListener('click', handleDocumentClick); + window.removeEventListener('resize', closeDirectoryList); + }; + }, []); + + const toggles: React.ReactNodeArray = []; + if (showBasePathToggle) { + toggles.push( + + + , + ); + } + if (showCompletedToggle) { + toggles.push( + + + , + ); + } + + return ( + + + { + if (textboxRef.current == null) { + return; + } + + const newDestination = textboxRef.current.value; + + if (onChange) { + onChange(newDestination); + } + + setDestination(newDestination); + }, + 100, + {leading: true}, + )} + onClick={(event) => event.nativeEvent.stopImmediatePropagation()} + placeholder={intl.formatMessage({ + id: 'torrents.add.destination.placeholder', + })} + ref={textboxRef}> + { + if (textboxRef.current != null) { + setDestination(textboxRef.current.value); + } + setIsDirectoryListOpen(!isDirectoryListOpen); + }}> + + + + event.nativeEvent.stopImmediatePropagation()} + overlayProps={{isInteractive: false}} + padding={false} + triggerRef={textboxRef}> + { + if (textboxRef.current != null) { + textboxRef.current.value = newDestination; + } + + setDestination(newDestination); + setIsDirectoryListOpen(isDirectory); + }} + /> + + + + + {toggles.length > 0 ? {toggles} : null} + + ); + }, +); + +FilesystemBrowserTextbox.defaultProps = { + label: undefined, + selectable: undefined, + suggested: undefined, + showBasePathToggle: false, + showCompletedToggle: false, + onChange: undefined, +}; + +export default FilesystemBrowserTextbox; diff --git a/client/src/javascript/components/general/form-elements/TagSelect.tsx b/client/src/javascript/components/general/form-elements/TagSelect.tsx new file mode 100644 index 000000000..f6f34bb60 --- /dev/null +++ b/client/src/javascript/components/general/form-elements/TagSelect.tsx @@ -0,0 +1,150 @@ +import classnames from 'classnames'; +import {FC, ReactNode, ReactNodeArray, useEffect, useRef, useState} from 'react'; +import {FormattedMessage} from 'react-intl'; +import {useKeyPressEvent} from 'react-use'; + +import type {TorrentProperties} from '@shared/types/Torrent'; + +import {ContextMenu, FormElementAddon, FormRowItem, Portal, SelectItem, Textbox} from '../../../ui'; +import Chevron from '../../../ui/icons/Chevron'; +import SettingStore from '../../../stores/SettingStore'; +import TorrentFilterStore from '../../../stores/TorrentFilterStore'; + +interface TagSelectProps { + id?: string; + label?: ReactNode; + defaultValue?: TorrentProperties['tags']; + placeholder?: string; + onTagSelected?: (tags: TorrentProperties['tags']) => void; +} + +const TagSelect: FC = ({defaultValue, placeholder, id, label, onTagSelected}: TagSelectProps) => { + const [isOpen, setIsOpen] = useState(false); + const [selectedTags, setSelectedTags] = useState>(defaultValue ?? []); + const formRowRef = useRef(null); + const menuRef = useRef(null); + const textboxRef = useRef(null); + + const classes = classnames('select form__element', { + 'select--is-open': isOpen, + }); + + useKeyPressEvent('Escape', (e) => { + e.preventDefault(); + setIsOpen(false); + }); + + useEffect(() => { + const handleDocumentClick = (e: Event) => { + if (!formRowRef.current?.contains((e.target as unknown) as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener('click', handleDocumentClick); + + return () => { + document.removeEventListener('click', handleDocumentClick); + }; + }, []); + + useEffect(() => { + if (textboxRef.current != null) { + textboxRef.current.value = selectedTags.join(); + } + if (onTagSelected) { + onTagSelected(selectedTags); + } + }, [selectedTags, onTagSelected]); + + return ( + + +
    + { + if (textboxRef.current != null) { + let selectedTagsArray = textboxRef.current.value + .split(',') + .map((tag) => tag.trim()) + .filter((tag) => tag.length > 0); + + if (textboxRef.current.value.trimEnd().endsWith(',')) { + // Ensures that the trailing ',' does not get removed automatically. + selectedTagsArray.push(''); + + // Deduplicate + selectedTagsArray = [...new Set(selectedTagsArray)]; + } + + setSelectedTags(selectedTagsArray); + } + }} + placeholder={placeholder} + ref={textboxRef}> + { + setIsOpen(!isOpen); + }} + className="select__indicator"> + + + + { + if (SettingStore.floodSettings.UITagSelectorMode !== 'single') { + event.nativeEvent.stopImmediatePropagation(); + } + }} + overlayProps={{isInteractive: false}} + ref={menuRef} + triggerRef={textboxRef}> + {[...new Set([...Object.keys(TorrentFilterStore.taxonomy.tagCounts), ...selectedTags])].reduce( + (accumulator: ReactNodeArray, tag) => { + if (tag === '') { + return accumulator; + } + + accumulator.push( + { + if (tag === 'untagged') { + setSelectedTags([]); + } else if (selectedTags.includes(tag)) { + setSelectedTags(selectedTags.filter((key) => key !== tag && key !== '')); + } else { + setSelectedTags([...selectedTags.filter((key) => key !== ''), tag]); + } + }}> + {tag === 'untagged' ? : tag} + , + ); + return accumulator; + }, + [], + )} + + + +
    +
    + ); +}; + +TagSelect.defaultProps = { + id: 'tags', + label: undefined, + defaultValue: undefined, + placeholder: undefined, + onTagSelected: undefined, +}; + +export default TagSelect; diff --git a/client/src/javascript/components/general/form-elements/TextboxRepeater.js b/client/src/javascript/components/general/form-elements/TextboxRepeater.js deleted file mode 100644 index 731e92500..000000000 --- a/client/src/javascript/components/general/form-elements/TextboxRepeater.js +++ /dev/null @@ -1,73 +0,0 @@ -import {FormElementAddon, FormRow, FormRowGroup, Textbox} from 'flood-ui-kit'; -import React from 'react'; - -import AddMini from '../../icons/AddMini'; -import RemoveMini from '../../icons/RemoveMini'; - -export default class TextboxRepeater extends React.PureComponent { - state = { - textboxes: this.props.defaultValues || [{id: 0, value: ''}], - }; - - idCounter = 0; - - getID() { - return ++this.idCounter; - } - - getTextboxes = () => - this.state.textboxes.map((textbox, index) => { - let removeButton = null; - - if (index > 0) { - removeButton = ( - { - this.handleTextboxRemove(index); - }}> - - - ); - } - - return ( - - - { - this.handleTextboxAdd(index); - }}> - - - {removeButton} - - - ); - }); - - handleTextboxAdd = index => { - this.setState(state => { - const textboxes = Object.assign([], state.textboxes); - textboxes.splice(index + 1, 0, {id: this.getID(), value: ''}); - return {textboxes}; - }); - }; - - handleTextboxRemove = index => { - this.setState(state => { - const textboxes = Object.assign([], state.textboxes); - textboxes.splice(index, 1); - return {textboxes}; - }); - }; - - render() { - return {this.getTextboxes()}; - } -} diff --git a/client/src/javascript/components/general/form-elements/TextboxRepeater.tsx b/client/src/javascript/components/general/form-elements/TextboxRepeater.tsx new file mode 100644 index 000000000..00cf04433 --- /dev/null +++ b/client/src/javascript/components/general/form-elements/TextboxRepeater.tsx @@ -0,0 +1,86 @@ +import {FC, ReactNode, useRef, useState} from 'react'; + +import {FormElementAddon, FormRow, FormRowGroup, Textbox} from '../../../ui'; +import AddMini from '../../icons/AddMini'; +import RemoveMini from '../../icons/RemoveMini'; + +export const getTextArray = (formData: Record, id: string) => + Object.keys(formData).reduce((accumulator: Array, formItemKey: string) => { + if (formItemKey.startsWith(id)) { + const text = formData[formItemKey]; + if (text != null) { + accumulator.push(text); + } + } + return accumulator; + }, []); + +type Textboxes = Array<{id: number; value: string}>; + +interface TextboxRepeaterProps { + defaultValues?: Textboxes; + id: number | string; + label?: ReactNode; + placeholder?: string; +} + +const TextboxRepeater: FC = ({defaultValues, id, label, placeholder}: TextboxRepeaterProps) => { + const idCounter = useRef(0); + const [textboxes, setTextboxes] = useState(defaultValues ?? [{id: 0, value: ''}]); + + return ( + + {textboxes.map((textbox, index) => { + let removeButton = null; + + if (index > 0) { + removeButton = ( + { + const newTextboxes = textboxes.slice(); + newTextboxes.splice(index, 1); + setTextboxes(newTextboxes); + }}> + + + ); + } + + return ( + + + { + idCounter.current += 1; + + const newTextboxes = textboxes.slice(); + newTextboxes.splice(index + 1, 0, { + id: idCounter.current, + value: '', + }); + setTextboxes(newTextboxes); + }}> + + + {removeButton} + + + ); + })} + + ); +}; + +TextboxRepeater.defaultProps = { + defaultValues: undefined, + label: undefined, + placeholder: undefined, +}; + +export default TextboxRepeater; diff --git a/client/src/javascript/components/icons/Active.js b/client/src/javascript/components/icons/Active.js deleted file mode 100644 index dbcb568ad..000000000 --- a/client/src/javascript/components/icons/Active.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Active extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Active.tsx b/client/src/javascript/components/icons/Active.tsx new file mode 100644 index 000000000..4023894af --- /dev/null +++ b/client/src/javascript/components/icons/Active.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Active extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Add.js b/client/src/javascript/components/icons/Add.js deleted file mode 100644 index 41e6e4acf..000000000 --- a/client/src/javascript/components/icons/Add.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class AddMini extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Add.tsx b/client/src/javascript/components/icons/Add.tsx new file mode 100644 index 000000000..2bf329871 --- /dev/null +++ b/client/src/javascript/components/icons/Add.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class AddMini extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/AddMini.js b/client/src/javascript/components/icons/AddMini.js deleted file mode 100644 index 171cfd059..000000000 --- a/client/src/javascript/components/icons/AddMini.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class AddMini extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/AddMini.tsx b/client/src/javascript/components/icons/AddMini.tsx new file mode 100644 index 000000000..4d72fab3d --- /dev/null +++ b/client/src/javascript/components/icons/AddMini.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class AddMini extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/All.js b/client/src/javascript/components/icons/All.js deleted file mode 100644 index f77164bb8..000000000 --- a/client/src/javascript/components/icons/All.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class All extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/All.tsx b/client/src/javascript/components/icons/All.tsx new file mode 100644 index 000000000..78bbd146c --- /dev/null +++ b/client/src/javascript/components/icons/All.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class All extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/ArrowIcon.js b/client/src/javascript/components/icons/ArrowIcon.js deleted file mode 100644 index 5e40803dd..000000000 --- a/client/src/javascript/components/icons/ArrowIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class ArrowIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/ArrowIcon.tsx b/client/src/javascript/components/icons/ArrowIcon.tsx new file mode 100644 index 000000000..f201c2aa6 --- /dev/null +++ b/client/src/javascript/components/icons/ArrowIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class ArrowIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/BaseIcon.js b/client/src/javascript/components/icons/BaseIcon.js deleted file mode 100644 index f93423848..000000000 --- a/client/src/javascript/components/icons/BaseIcon.js +++ /dev/null @@ -1,24 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; - -export default class BaseIcon extends React.Component { - static propTypes = { - size: PropTypes.string, - viewBox: PropTypes.string, - }; - - static defaultProps = { - className: '', - viewBox: '0 0 60 60', - }; - - getViewBox() { - let {viewBox} = this.props; - - if (this.props.size && this.props.size === 'mini') { - viewBox = '0 0 8 8'; - } - - return viewBox; - } -} diff --git a/client/src/javascript/components/icons/BaseIcon.tsx b/client/src/javascript/components/icons/BaseIcon.tsx new file mode 100644 index 000000000..d824a31dd --- /dev/null +++ b/client/src/javascript/components/icons/BaseIcon.tsx @@ -0,0 +1,24 @@ +import {PureComponent} from 'react'; + +interface BaseIconProps { + className?: string; + size?: string; + viewBox?: string; +} + +export default class BaseIcon extends PureComponent { + static defaultProps = { + className: '', + viewBox: '0 0 60 60', + }; + + getViewBox() { + let {viewBox} = this.props; + + if (this.props.size && this.props.size === 'mini') { + viewBox = '0 0 8 8'; + } + + return viewBox; + } +} diff --git a/client/src/javascript/components/icons/CalendarCreatedIcon.js b/client/src/javascript/components/icons/CalendarCreatedIcon.js deleted file mode 100644 index 5f880c4c9..000000000 --- a/client/src/javascript/components/icons/CalendarCreatedIcon.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class CalendarCreatedIcon extends BaseIcon { - render() { - return ( - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/CalendarCreatedIcon.tsx b/client/src/javascript/components/icons/CalendarCreatedIcon.tsx new file mode 100644 index 000000000..22f27543d --- /dev/null +++ b/client/src/javascript/components/icons/CalendarCreatedIcon.tsx @@ -0,0 +1,14 @@ +import BaseIcon from './BaseIcon'; + +export default class CalendarCreatedIcon extends BaseIcon { + render() { + return ( + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/CalendarIcon.js b/client/src/javascript/components/icons/CalendarIcon.js deleted file mode 100644 index c00a6a4a1..000000000 --- a/client/src/javascript/components/icons/CalendarIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class CalendarIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/CalendarIcon.tsx b/client/src/javascript/components/icons/CalendarIcon.tsx new file mode 100644 index 000000000..44767a469 --- /dev/null +++ b/client/src/javascript/components/icons/CalendarIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class CalendarIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Checkmark.js b/client/src/javascript/components/icons/Checkmark.js deleted file mode 100644 index 37762e182..000000000 --- a/client/src/javascript/components/icons/Checkmark.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Checkmark extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Checkmark.tsx b/client/src/javascript/components/icons/Checkmark.tsx new file mode 100644 index 000000000..1c38e1476 --- /dev/null +++ b/client/src/javascript/components/icons/Checkmark.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Checkmark extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/ChevronLeftIcon.js b/client/src/javascript/components/icons/ChevronLeftIcon.js deleted file mode 100644 index 778b12ba2..000000000 --- a/client/src/javascript/components/icons/ChevronLeftIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class ChevronLeftIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/ChevronLeftIcon.tsx b/client/src/javascript/components/icons/ChevronLeftIcon.tsx new file mode 100644 index 000000000..0dd73e900 --- /dev/null +++ b/client/src/javascript/components/icons/ChevronLeftIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class ChevronLeftIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/ChevronRightIcon.js b/client/src/javascript/components/icons/ChevronRightIcon.js deleted file mode 100644 index 7461c6591..000000000 --- a/client/src/javascript/components/icons/ChevronRightIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class ChevronRightIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/ChevronRightIcon.tsx b/client/src/javascript/components/icons/ChevronRightIcon.tsx new file mode 100644 index 000000000..d567b8e2a --- /dev/null +++ b/client/src/javascript/components/icons/ChevronRightIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class ChevronRightIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/CircleCheckmarkIcon.js b/client/src/javascript/components/icons/CircleCheckmarkIcon.js deleted file mode 100644 index 9e9d62ed4..000000000 --- a/client/src/javascript/components/icons/CircleCheckmarkIcon.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class CircleCheckmarkIcon extends BaseIcon { - render() { - return ( - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/CircleCheckmarkIcon.tsx b/client/src/javascript/components/icons/CircleCheckmarkIcon.tsx new file mode 100644 index 000000000..3dca9b88b --- /dev/null +++ b/client/src/javascript/components/icons/CircleCheckmarkIcon.tsx @@ -0,0 +1,16 @@ +import BaseIcon from './BaseIcon'; + +export default class CircleCheckmarkIcon extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/CircleExclamationIcon.js b/client/src/javascript/components/icons/CircleExclamationIcon.js deleted file mode 100644 index 2f5a262fc..000000000 --- a/client/src/javascript/components/icons/CircleExclamationIcon.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class CircleExclamationIcon extends BaseIcon { - render() { - return ( - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/CircleExclamationIcon.tsx b/client/src/javascript/components/icons/CircleExclamationIcon.tsx new file mode 100644 index 000000000..e913a0b2b --- /dev/null +++ b/client/src/javascript/components/icons/CircleExclamationIcon.tsx @@ -0,0 +1,16 @@ +import BaseIcon from './BaseIcon'; + +export default class CircleExclamationIcon extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/CircleIcon.js b/client/src/javascript/components/icons/CircleIcon.js deleted file mode 100644 index 6466efc86..000000000 --- a/client/src/javascript/components/icons/CircleIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Circle extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/CircleIcon.tsx b/client/src/javascript/components/icons/CircleIcon.tsx new file mode 100644 index 000000000..4863b2467 --- /dev/null +++ b/client/src/javascript/components/icons/CircleIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Circle extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/ClipboardIcon.js b/client/src/javascript/components/icons/ClipboardIcon.js deleted file mode 100644 index 0e2269037..000000000 --- a/client/src/javascript/components/icons/ClipboardIcon.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class ClipboardIcon extends BaseIcon { - render() { - return ( - - - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/ClipboardIcon.tsx b/client/src/javascript/components/icons/ClipboardIcon.tsx new file mode 100644 index 000000000..9ecc4f32c --- /dev/null +++ b/client/src/javascript/components/icons/ClipboardIcon.tsx @@ -0,0 +1,15 @@ +import BaseIcon from './BaseIcon'; + +export default class ClipboardIcon extends BaseIcon { + render() { + return ( + + + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/ClockIcon.js b/client/src/javascript/components/icons/ClockIcon.js deleted file mode 100644 index 19bfac22a..000000000 --- a/client/src/javascript/components/icons/ClockIcon.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class ClockIcon extends BaseIcon { - render() { - return ( - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/ClockIcon.tsx b/client/src/javascript/components/icons/ClockIcon.tsx new file mode 100644 index 000000000..9f7b0fe1a --- /dev/null +++ b/client/src/javascript/components/icons/ClockIcon.tsx @@ -0,0 +1,12 @@ +import BaseIcon from './BaseIcon'; + +export default class ClockIcon extends BaseIcon { + render() { + return ( + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Close.js b/client/src/javascript/components/icons/Close.js deleted file mode 100644 index 6c330b241..000000000 --- a/client/src/javascript/components/icons/Close.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Close extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Close.tsx b/client/src/javascript/components/icons/Close.tsx new file mode 100644 index 000000000..89cd35cd4 --- /dev/null +++ b/client/src/javascript/components/icons/Close.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Close extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/CommentIcon.js b/client/src/javascript/components/icons/CommentIcon.js deleted file mode 100644 index 2004234cd..000000000 --- a/client/src/javascript/components/icons/CommentIcon.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class CommentIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/CommentIcon.tsx b/client/src/javascript/components/icons/CommentIcon.tsx new file mode 100644 index 000000000..f932efd9b --- /dev/null +++ b/client/src/javascript/components/icons/CommentIcon.tsx @@ -0,0 +1,13 @@ +import BaseIcon from './BaseIcon'; + +export default class CommentIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Completed.js b/client/src/javascript/components/icons/Completed.js deleted file mode 100644 index 99816e6f5..000000000 --- a/client/src/javascript/components/icons/Completed.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Completed extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Completed.tsx b/client/src/javascript/components/icons/Completed.tsx new file mode 100644 index 000000000..f353642c2 --- /dev/null +++ b/client/src/javascript/components/icons/Completed.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Completed extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/CountryFlagIcon.tsx b/client/src/javascript/components/icons/CountryFlagIcon.tsx new file mode 100644 index 000000000..2b83d80b1 --- /dev/null +++ b/client/src/javascript/components/icons/CountryFlagIcon.tsx @@ -0,0 +1,47 @@ +import {FC} from 'react'; + +const flagsCache: Record = {}; + +const getFlag = (countryCode?: string): string | null => { + if (countryCode == null) { + return null; + } + + if (flagsCache[countryCode] !== undefined) { + return flagsCache[countryCode]; + } + + const loadFlag = async () => { + let flag: string | null = null; + await import(/* webpackChunkName: 'flag' */ `../../../images/flags/${countryCode.toLowerCase()}.png`) + .then( + ({default: image}: {default: string}) => { + flag = image; + }, + () => { + flag = null; + }, + ) + .finally(() => { + flagsCache[countryCode] = flag; + }); + return flag; + }; + + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw loadFlag(); +}; + +interface CountryFlagIconProps { + countryCode: string; +} + +const CountryFlagIcon: FC = ({countryCode}: CountryFlagIconProps) => { + const flag = getFlag(countryCode); + if (flag == null) { + return null; + } + return {countryCode}; +}; + +export default CountryFlagIcon; diff --git a/client/src/javascript/components/icons/DetailNotAvailableIcon.js b/client/src/javascript/components/icons/DetailNotAvailableIcon.js deleted file mode 100644 index 58a2a991f..000000000 --- a/client/src/javascript/components/icons/DetailNotAvailableIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class DetailNotAvailableIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/DetailNotAvailableIcon.tsx b/client/src/javascript/components/icons/DetailNotAvailableIcon.tsx new file mode 100644 index 000000000..62bcde335 --- /dev/null +++ b/client/src/javascript/components/icons/DetailNotAvailableIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class DetailNotAvailableIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Disk.js b/client/src/javascript/components/icons/Disk.js deleted file mode 100644 index 3692e5095..000000000 --- a/client/src/javascript/components/icons/Disk.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Disk extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Disk.tsx b/client/src/javascript/components/icons/Disk.tsx new file mode 100644 index 000000000..64982acf3 --- /dev/null +++ b/client/src/javascript/components/icons/Disk.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Disk extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/DiskIcon.js b/client/src/javascript/components/icons/DiskIcon.js deleted file mode 100644 index 3aec041eb..000000000 --- a/client/src/javascript/components/icons/DiskIcon.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class DiskIcon extends BaseIcon { - render() { - return ( - - - - - - - - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/DiskIcon.tsx b/client/src/javascript/components/icons/DiskIcon.tsx new file mode 100644 index 000000000..0788f0973 --- /dev/null +++ b/client/src/javascript/components/icons/DiskIcon.tsx @@ -0,0 +1,23 @@ +import BaseIcon from './BaseIcon'; + +export default class DiskIcon extends BaseIcon { + render() { + return ( + + + + + + + + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/DotsMini.js b/client/src/javascript/components/icons/DotsMini.js deleted file mode 100644 index 300a1bec5..000000000 --- a/client/src/javascript/components/icons/DotsMini.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class DotsMini extends BaseIcon { - render() { - return ( - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/DotsMini.tsx b/client/src/javascript/components/icons/DotsMini.tsx new file mode 100644 index 000000000..aa70504bc --- /dev/null +++ b/client/src/javascript/components/icons/DotsMini.tsx @@ -0,0 +1,13 @@ +import BaseIcon from './BaseIcon'; + +export default class DotsMini extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Download.js b/client/src/javascript/components/icons/Download.js deleted file mode 100644 index ef641ac8f..000000000 --- a/client/src/javascript/components/icons/Download.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Download extends BaseIcon { - render() { - return ( - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Download.tsx b/client/src/javascript/components/icons/Download.tsx new file mode 100644 index 000000000..56ffaeae4 --- /dev/null +++ b/client/src/javascript/components/icons/Download.tsx @@ -0,0 +1,12 @@ +import BaseIcon from './BaseIcon'; + +export default class Download extends BaseIcon { + render() { + return ( + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/DownloadSmall.js b/client/src/javascript/components/icons/DownloadSmall.js deleted file mode 100644 index f2bf889bc..000000000 --- a/client/src/javascript/components/icons/DownloadSmall.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class DownloadSmall extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/DownloadSmall.tsx b/client/src/javascript/components/icons/DownloadSmall.tsx new file mode 100644 index 000000000..418d85f88 --- /dev/null +++ b/client/src/javascript/components/icons/DownloadSmall.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class DownloadSmall extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/DownloadThickIcon.js b/client/src/javascript/components/icons/DownloadThickIcon.js deleted file mode 100644 index f2115ffc4..000000000 --- a/client/src/javascript/components/icons/DownloadThickIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class DownloadThickIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/DownloadThickIcon.tsx b/client/src/javascript/components/icons/DownloadThickIcon.tsx new file mode 100644 index 000000000..00a20b792 --- /dev/null +++ b/client/src/javascript/components/icons/DownloadThickIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class DownloadThickIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/ETA.js b/client/src/javascript/components/icons/ETA.js deleted file mode 100644 index 92490c9ea..000000000 --- a/client/src/javascript/components/icons/ETA.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class ETA extends BaseIcon { - render() { - return ( - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/ETA.tsx b/client/src/javascript/components/icons/ETA.tsx new file mode 100644 index 000000000..103690f60 --- /dev/null +++ b/client/src/javascript/components/icons/ETA.tsx @@ -0,0 +1,18 @@ +import BaseIcon from './BaseIcon'; + +export default class ETA extends BaseIcon { + render() { + return ( + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Edit.js b/client/src/javascript/components/icons/Edit.js deleted file mode 100644 index 9b1a09fc5..000000000 --- a/client/src/javascript/components/icons/Edit.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Edit extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Edit.tsx b/client/src/javascript/components/icons/Edit.tsx new file mode 100644 index 000000000..8de5df93b --- /dev/null +++ b/client/src/javascript/components/icons/Edit.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Edit extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/ErrorIcon.js b/client/src/javascript/components/icons/ErrorIcon.js deleted file mode 100644 index f7be7a630..000000000 --- a/client/src/javascript/components/icons/ErrorIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Error extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/ErrorIcon.tsx b/client/src/javascript/components/icons/ErrorIcon.tsx new file mode 100644 index 000000000..9d6ae9d87 --- /dev/null +++ b/client/src/javascript/components/icons/ErrorIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Error extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/FeedIcon.js b/client/src/javascript/components/icons/FeedIcon.js deleted file mode 100644 index c0f06a554..000000000 --- a/client/src/javascript/components/icons/FeedIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class FeedIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/FeedIcon.tsx b/client/src/javascript/components/icons/FeedIcon.tsx new file mode 100644 index 000000000..3f0000f6b --- /dev/null +++ b/client/src/javascript/components/icons/FeedIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class FeedIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/File.js b/client/src/javascript/components/icons/File.js deleted file mode 100644 index a3c885674..000000000 --- a/client/src/javascript/components/icons/File.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class File extends BaseIcon { - render() { - return ( - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/File.tsx b/client/src/javascript/components/icons/File.tsx new file mode 100644 index 000000000..ab7d12b25 --- /dev/null +++ b/client/src/javascript/components/icons/File.tsx @@ -0,0 +1,13 @@ +import BaseIcon from './BaseIcon'; + +export default class File extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Files.js b/client/src/javascript/components/icons/Files.js deleted file mode 100644 index b5235962c..000000000 --- a/client/src/javascript/components/icons/Files.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Files extends BaseIcon { - render() { - return ( - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Files.tsx b/client/src/javascript/components/icons/Files.tsx new file mode 100644 index 000000000..76d4698ca --- /dev/null +++ b/client/src/javascript/components/icons/Files.tsx @@ -0,0 +1,22 @@ +import BaseIcon from './BaseIcon'; + +export default class Files extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/FolderClosedOutlined.js b/client/src/javascript/components/icons/FolderClosedOutlined.js deleted file mode 100644 index 04258db17..000000000 --- a/client/src/javascript/components/icons/FolderClosedOutlined.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class FolderClosedOutline extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/FolderClosedOutlined.tsx b/client/src/javascript/components/icons/FolderClosedOutlined.tsx new file mode 100644 index 000000000..9b2fe4190 --- /dev/null +++ b/client/src/javascript/components/icons/FolderClosedOutlined.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class FolderClosedOutline extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/FolderClosedSolid.js b/client/src/javascript/components/icons/FolderClosedSolid.js deleted file mode 100644 index 33a8636d9..000000000 --- a/client/src/javascript/components/icons/FolderClosedSolid.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class FolderClosedOutline extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/FolderClosedSolid.tsx b/client/src/javascript/components/icons/FolderClosedSolid.tsx new file mode 100644 index 000000000..f912ba7ed --- /dev/null +++ b/client/src/javascript/components/icons/FolderClosedSolid.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class FolderClosedOutline extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/FolderOpenOutlined.js b/client/src/javascript/components/icons/FolderOpenOutlined.js deleted file mode 100644 index 8738478c7..000000000 --- a/client/src/javascript/components/icons/FolderOpenOutlined.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class FolderOpenOutlined extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/FolderOpenOutlined.tsx b/client/src/javascript/components/icons/FolderOpenOutlined.tsx new file mode 100644 index 000000000..bb8197efe --- /dev/null +++ b/client/src/javascript/components/icons/FolderOpenOutlined.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class FolderOpenOutlined extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/FolderOpenSolid.js b/client/src/javascript/components/icons/FolderOpenSolid.js deleted file mode 100644 index 7c3dbcdbb..000000000 --- a/client/src/javascript/components/icons/FolderOpenSolid.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class FolderOpenSolid extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/FolderOpenSolid.tsx b/client/src/javascript/components/icons/FolderOpenSolid.tsx new file mode 100644 index 000000000..5acbd7438 --- /dev/null +++ b/client/src/javascript/components/icons/FolderOpenSolid.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class FolderOpenSolid extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/HashIcon.js b/client/src/javascript/components/icons/HashIcon.js deleted file mode 100644 index baf72ad50..000000000 --- a/client/src/javascript/components/icons/HashIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class HashIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/HashIcon.tsx b/client/src/javascript/components/icons/HashIcon.tsx new file mode 100644 index 000000000..ffa147526 --- /dev/null +++ b/client/src/javascript/components/icons/HashIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class HashIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Inactive.js b/client/src/javascript/components/icons/Inactive.js deleted file mode 100644 index 88af526cb..000000000 --- a/client/src/javascript/components/icons/Inactive.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Inactive extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Inactive.tsx b/client/src/javascript/components/icons/Inactive.tsx new file mode 100644 index 000000000..60f35606c --- /dev/null +++ b/client/src/javascript/components/icons/Inactive.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Inactive extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/InfinityIcon.js b/client/src/javascript/components/icons/InfinityIcon.js deleted file mode 100644 index f5f84d6d6..000000000 --- a/client/src/javascript/components/icons/InfinityIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class InfinityIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/InfinityIcon.tsx b/client/src/javascript/components/icons/InfinityIcon.tsx new file mode 100644 index 000000000..d9a2c43fd --- /dev/null +++ b/client/src/javascript/components/icons/InfinityIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class InfinityIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/InformationIcon.js b/client/src/javascript/components/icons/InformationIcon.js deleted file mode 100644 index 809ed6982..000000000 --- a/client/src/javascript/components/icons/InformationIcon.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class InformationIcon extends BaseIcon { - render() { - return ( - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/InformationIcon.tsx b/client/src/javascript/components/icons/InformationIcon.tsx new file mode 100644 index 000000000..de3b58aea --- /dev/null +++ b/client/src/javascript/components/icons/InformationIcon.tsx @@ -0,0 +1,19 @@ +import BaseIcon from './BaseIcon'; + +export default class InformationIcon extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Limits.js b/client/src/javascript/components/icons/Limits.js deleted file mode 100644 index 1236faef7..000000000 --- a/client/src/javascript/components/icons/Limits.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Limits extends BaseIcon { - render() { - return ( - - - - - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Limits.tsx b/client/src/javascript/components/icons/Limits.tsx new file mode 100644 index 000000000..4bc706367 --- /dev/null +++ b/client/src/javascript/components/icons/Limits.tsx @@ -0,0 +1,29 @@ +import BaseIcon from './BaseIcon'; + +export default class Limits extends BaseIcon { + render() { + return ( + + + + + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/LoadingIndicatorDots.js b/client/src/javascript/components/icons/LoadingIndicatorDots.js deleted file mode 100644 index 5bd3b9258..000000000 --- a/client/src/javascript/components/icons/LoadingIndicatorDots.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class LoadingIndicatorDots extends BaseIcon { - render() { - return ( - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/LoadingIndicatorDots.tsx b/client/src/javascript/components/icons/LoadingIndicatorDots.tsx new file mode 100644 index 000000000..faefbbdb0 --- /dev/null +++ b/client/src/javascript/components/icons/LoadingIndicatorDots.tsx @@ -0,0 +1,24 @@ +import BaseIcon from './BaseIcon'; + +export default class LoadingIndicatorDots extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/LockIcon.js b/client/src/javascript/components/icons/LockIcon.js deleted file mode 100644 index d3ce8f686..000000000 --- a/client/src/javascript/components/icons/LockIcon.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class LockIcon extends BaseIcon { - render() { - return ( - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/LockIcon.tsx b/client/src/javascript/components/icons/LockIcon.tsx new file mode 100644 index 000000000..146f678ae --- /dev/null +++ b/client/src/javascript/components/icons/LockIcon.tsx @@ -0,0 +1,15 @@ +import BaseIcon from './BaseIcon'; + +export default class LockIcon extends BaseIcon { + render() { + return ( + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Logout.js b/client/src/javascript/components/icons/Logout.js deleted file mode 100644 index 6f21dfaef..000000000 --- a/client/src/javascript/components/icons/Logout.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Logout extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Logout.tsx b/client/src/javascript/components/icons/Logout.tsx new file mode 100644 index 000000000..213e8c1bf --- /dev/null +++ b/client/src/javascript/components/icons/Logout.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Logout extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/MenuIcon.tsx b/client/src/javascript/components/icons/MenuIcon.tsx new file mode 100644 index 000000000..d7b8249d6 --- /dev/null +++ b/client/src/javascript/components/icons/MenuIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Menu extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/NotificationIcon.js b/client/src/javascript/components/icons/NotificationIcon.js deleted file mode 100644 index a5db7da9d..000000000 --- a/client/src/javascript/components/icons/NotificationIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class NotificationIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/NotificationIcon.tsx b/client/src/javascript/components/icons/NotificationIcon.tsx new file mode 100644 index 000000000..dbc149b95 --- /dev/null +++ b/client/src/javascript/components/icons/NotificationIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class NotificationIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/PeersIcon.js b/client/src/javascript/components/icons/PeersIcon.js deleted file mode 100644 index ce1e694d5..000000000 --- a/client/src/javascript/components/icons/PeersIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class PeersIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/PeersIcon.tsx b/client/src/javascript/components/icons/PeersIcon.tsx new file mode 100644 index 000000000..6c8b87618 --- /dev/null +++ b/client/src/javascript/components/icons/PeersIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class PeersIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/RadarIcon.js b/client/src/javascript/components/icons/RadarIcon.js deleted file mode 100644 index 200181e94..000000000 --- a/client/src/javascript/components/icons/RadarIcon.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class RadarIcon extends BaseIcon { - render() { - return ( - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/RadarIcon.tsx b/client/src/javascript/components/icons/RadarIcon.tsx new file mode 100644 index 000000000..0fedb7212 --- /dev/null +++ b/client/src/javascript/components/icons/RadarIcon.tsx @@ -0,0 +1,13 @@ +import BaseIcon from './BaseIcon'; + +export default class RadarIcon extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/RadioDot.js b/client/src/javascript/components/icons/RadioDot.js deleted file mode 100644 index 0ca183d9d..000000000 --- a/client/src/javascript/components/icons/RadioDot.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class RadioDot extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/RadioDot.tsx b/client/src/javascript/components/icons/RadioDot.tsx new file mode 100644 index 000000000..f698830c8 --- /dev/null +++ b/client/src/javascript/components/icons/RadioDot.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class RadioDot extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Ratio.js b/client/src/javascript/components/icons/Ratio.js deleted file mode 100644 index a3e4e045e..000000000 --- a/client/src/javascript/components/icons/Ratio.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Ratio extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Ratio.tsx b/client/src/javascript/components/icons/Ratio.tsx new file mode 100644 index 000000000..98dd65968 --- /dev/null +++ b/client/src/javascript/components/icons/Ratio.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Ratio extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/RatioIcon.js b/client/src/javascript/components/icons/RatioIcon.js deleted file mode 100644 index cd56779be..000000000 --- a/client/src/javascript/components/icons/RatioIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class RatioIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/RatioIcon.tsx b/client/src/javascript/components/icons/RatioIcon.tsx new file mode 100644 index 000000000..83dd28cbd --- /dev/null +++ b/client/src/javascript/components/icons/RatioIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class RatioIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Remove.js b/client/src/javascript/components/icons/Remove.js deleted file mode 100644 index 85a7b01f9..000000000 --- a/client/src/javascript/components/icons/Remove.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class AddMini extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Remove.tsx b/client/src/javascript/components/icons/Remove.tsx new file mode 100644 index 000000000..53208be24 --- /dev/null +++ b/client/src/javascript/components/icons/Remove.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class AddMini extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/RemoveMini.js b/client/src/javascript/components/icons/RemoveMini.js deleted file mode 100644 index a9b915828..000000000 --- a/client/src/javascript/components/icons/RemoveMini.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class RemoveMini extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/RemoveMini.tsx b/client/src/javascript/components/icons/RemoveMini.tsx new file mode 100644 index 000000000..48649bba3 --- /dev/null +++ b/client/src/javascript/components/icons/RemoveMini.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class RemoveMini extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Search.js b/client/src/javascript/components/icons/Search.js deleted file mode 100644 index dcf97c9ee..000000000 --- a/client/src/javascript/components/icons/Search.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Search extends BaseIcon { - render() { - return ( - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Search.tsx b/client/src/javascript/components/icons/Search.tsx new file mode 100644 index 000000000..c0d868004 --- /dev/null +++ b/client/src/javascript/components/icons/Search.tsx @@ -0,0 +1,18 @@ +import BaseIcon from './BaseIcon'; + +export default class Search extends BaseIcon { + render() { + return ( + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/SeedsIcon.js b/client/src/javascript/components/icons/SeedsIcon.js deleted file mode 100644 index c3d1d7fb2..000000000 --- a/client/src/javascript/components/icons/SeedsIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class SeedsIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/SeedsIcon.tsx b/client/src/javascript/components/icons/SeedsIcon.tsx new file mode 100644 index 000000000..40d66939a --- /dev/null +++ b/client/src/javascript/components/icons/SeedsIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class SeedsIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/SettingsIcon.js b/client/src/javascript/components/icons/SettingsIcon.js deleted file mode 100644 index 35299f18e..000000000 --- a/client/src/javascript/components/icons/SettingsIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class SettingsIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/SettingsIcon.tsx b/client/src/javascript/components/icons/SettingsIcon.tsx new file mode 100644 index 000000000..caf5719b9 --- /dev/null +++ b/client/src/javascript/components/icons/SettingsIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class SettingsIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/SpinnerIcon.js b/client/src/javascript/components/icons/SpinnerIcon.js deleted file mode 100644 index 7e1e8bc20..000000000 --- a/client/src/javascript/components/icons/SpinnerIcon.js +++ /dev/null @@ -1,39 +0,0 @@ -import _ from 'lodash'; -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class SpinnerIcon extends BaseIcon { - constructor(...iconConfig) { - super(...iconConfig); - - this.id = _.uniqueId(); - } - - getViewBox() { - return '0 0 128 128'; - } - - render() { - const maskID = `icon--spinner__mask-id--${this.id}`; - - return ( - - - - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/SpinnerIcon.tsx b/client/src/javascript/components/icons/SpinnerIcon.tsx new file mode 100644 index 000000000..faf6ee52e --- /dev/null +++ b/client/src/javascript/components/icons/SpinnerIcon.tsx @@ -0,0 +1,34 @@ +import uniqueId from 'lodash/uniqueId'; + +import BaseIcon from './BaseIcon'; + +export default class SpinnerIcon extends BaseIcon { + id = uniqueId(); + + static getViewBox() { + return '0 0 128 128'; + } + + render() { + const maskID = `icon--spinner__mask-id--${this.id}`; + + return ( + + + + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/StartIcon.js b/client/src/javascript/components/icons/StartIcon.js deleted file mode 100644 index 3d33bb1c7..000000000 --- a/client/src/javascript/components/icons/StartIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Start extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/StartIcon.tsx b/client/src/javascript/components/icons/StartIcon.tsx new file mode 100644 index 000000000..ede6fb003 --- /dev/null +++ b/client/src/javascript/components/icons/StartIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Start extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/StopIcon.js b/client/src/javascript/components/icons/StopIcon.js deleted file mode 100644 index 365cd3ecb..000000000 --- a/client/src/javascript/components/icons/StopIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Stop extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/StopIcon.tsx b/client/src/javascript/components/icons/StopIcon.tsx new file mode 100644 index 000000000..beaa6cd13 --- /dev/null +++ b/client/src/javascript/components/icons/StopIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class Stop extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/icons/ThemeSwitchIcon.tsx b/client/src/javascript/components/icons/ThemeSwitchIcon.tsx new file mode 100644 index 000000000..141a56b17 --- /dev/null +++ b/client/src/javascript/components/icons/ThemeSwitchIcon.tsx @@ -0,0 +1,16 @@ +import BaseIcon from './BaseIcon'; + +export default class ThemeSwitchIcon extends BaseIcon { + render() { + // Material Design Icon: brightness_6 - Apache 2.0 + return ( + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/TrackerMessageIcon.js b/client/src/javascript/components/icons/TrackerMessageIcon.js deleted file mode 100644 index 42835543f..000000000 --- a/client/src/javascript/components/icons/TrackerMessageIcon.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class TrackerMessageIcon extends BaseIcon { - render() { - return ( - - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/TrackerMessageIcon.tsx b/client/src/javascript/components/icons/TrackerMessageIcon.tsx new file mode 100644 index 000000000..018aa5fd2 --- /dev/null +++ b/client/src/javascript/components/icons/TrackerMessageIcon.tsx @@ -0,0 +1,13 @@ +import BaseIcon from './BaseIcon'; + +export default class TrackerMessageIcon extends BaseIcon { + render() { + return ( + + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/Upload.js b/client/src/javascript/components/icons/Upload.js deleted file mode 100644 index d8b452758..000000000 --- a/client/src/javascript/components/icons/Upload.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class Upload extends BaseIcon { - render() { - return ( - - - - - ); - } -} diff --git a/client/src/javascript/components/icons/Upload.tsx b/client/src/javascript/components/icons/Upload.tsx new file mode 100644 index 000000000..52fe4e8fe --- /dev/null +++ b/client/src/javascript/components/icons/Upload.tsx @@ -0,0 +1,12 @@ +import BaseIcon from './BaseIcon'; + +export default class Upload extends BaseIcon { + render() { + return ( + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/UploadSmall.tsx b/client/src/javascript/components/icons/UploadSmall.tsx new file mode 100644 index 000000000..2f08e95b8 --- /dev/null +++ b/client/src/javascript/components/icons/UploadSmall.tsx @@ -0,0 +1,12 @@ +import BaseIcon from './BaseIcon'; + +export default class UploadSmall extends BaseIcon { + render() { + return ( + + + + + ); + } +} diff --git a/client/src/javascript/components/icons/UploadThickIcon.js b/client/src/javascript/components/icons/UploadThickIcon.js deleted file mode 100644 index f3502fb4c..000000000 --- a/client/src/javascript/components/icons/UploadThickIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -import BaseIcon from './BaseIcon'; - -export default class UploadThickIcon extends BaseIcon { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/icons/UploadThickIcon.tsx b/client/src/javascript/components/icons/UploadThickIcon.tsx new file mode 100644 index 000000000..b14385014 --- /dev/null +++ b/client/src/javascript/components/icons/UploadThickIcon.tsx @@ -0,0 +1,11 @@ +import BaseIcon from './BaseIcon'; + +export default class UploadThickIcon extends BaseIcon { + render() { + return ( + + + + ); + } +} diff --git a/client/src/javascript/components/layout/ApplicationContent.js b/client/src/javascript/components/layout/ApplicationContent.js deleted file mode 100644 index 325c10c99..000000000 --- a/client/src/javascript/components/layout/ApplicationContent.js +++ /dev/null @@ -1,14 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; - -class ApplicationContent extends React.Component { - static propTypes = { - children: PropTypes.node, - }; - - render() { - return
    {this.props.children}
    ; - } -} - -export default ApplicationContent; diff --git a/client/src/javascript/components/layout/ApplicationContent.tsx b/client/src/javascript/components/layout/ApplicationContent.tsx new file mode 100644 index 000000000..481f312a3 --- /dev/null +++ b/client/src/javascript/components/layout/ApplicationContent.tsx @@ -0,0 +1,11 @@ +import {FC, ReactNode} from 'react'; + +interface ApplicationContentProps { + children: ReactNode; +} + +const ApplicationContent: FC = ({children}: ApplicationContentProps) => ( +
    {children}
    +); + +export default ApplicationContent; diff --git a/client/src/javascript/components/layout/ApplicationPanel.js b/client/src/javascript/components/layout/ApplicationPanel.js deleted file mode 100644 index 2a059b9af..000000000 --- a/client/src/javascript/components/layout/ApplicationPanel.js +++ /dev/null @@ -1,26 +0,0 @@ -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -class ApplicationContent extends React.Component { - static propTypes = { - children: PropTypes.node, - className: PropTypes.string, - modifier: PropTypes.string, - }; - - static defaultProps = { - baseClassName: 'application__panel', - }; - - render() { - const classes = classnames(this.props.baseClassName, { - [`${this.props.baseClassName}--${this.props.modifier}`]: this.props.baseClassName, - [this.props.className]: this.props.className, - }); - - return
    {this.props.children}
    ; - } -} - -export default ApplicationContent; diff --git a/client/src/javascript/components/layout/ApplicationPanel.tsx b/client/src/javascript/components/layout/ApplicationPanel.tsx new file mode 100644 index 000000000..247b649e2 --- /dev/null +++ b/client/src/javascript/components/layout/ApplicationPanel.tsx @@ -0,0 +1,26 @@ +import classnames from 'classnames'; +import {FC, ReactNode} from 'react'; + +interface ApplicationContentProps { + children: ReactNode; + baseClassName?: string; + className: string; + modifier: string; +} + +const ApplicationContent: FC = (props: ApplicationContentProps) => { + const {children, baseClassName, className, modifier} = props; + + const classes = classnames(baseClassName, { + [`${baseClassName}--${modifier}`]: baseClassName, + [className]: className, + }); + + return
    {children}
    ; +}; + +ApplicationContent.defaultProps = { + baseClassName: 'application__panel', +}; + +export default ApplicationContent; diff --git a/client/src/javascript/components/layout/ApplicationView.js b/client/src/javascript/components/layout/ApplicationView.js deleted file mode 100644 index cb3d9555d..000000000 --- a/client/src/javascript/components/layout/ApplicationView.js +++ /dev/null @@ -1,20 +0,0 @@ -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -class ApplicationView extends React.Component { - static propTypes = { - children: PropTypes.node, - modifier: PropTypes.string, - }; - - render() { - const classes = classnames('application__view', { - [`application__view--${this.props.modifier}`]: this.props.modifier != null, - }); - - return
    {this.props.children}
    ; - } -} - -export default ApplicationView; diff --git a/client/src/javascript/components/layout/ApplicationView.tsx b/client/src/javascript/components/layout/ApplicationView.tsx new file mode 100644 index 000000000..22d96abf5 --- /dev/null +++ b/client/src/javascript/components/layout/ApplicationView.tsx @@ -0,0 +1,23 @@ +import classnames from 'classnames'; +import {FC, ReactNode} from 'react'; + +interface ApplicationViewProps { + children: ReactNode; + modifier?: string; +} + +const ApplicationView: FC = (props: ApplicationViewProps) => { + const {children, modifier} = props; + + const classes = classnames('application__view', { + [`application__view--${modifier}`]: modifier != null, + }); + + return
    {children}
    ; +}; + +ApplicationView.defaultProps = { + modifier: undefined, +}; + +export default ApplicationView; diff --git a/client/src/javascript/components/modals/Modal.js b/client/src/javascript/components/modals/Modal.js deleted file mode 100644 index 86fb7a326..000000000 --- a/client/src/javascript/components/modals/Modal.js +++ /dev/null @@ -1,128 +0,0 @@ -import classnames from 'classnames'; -import React from 'react'; - -import ModalActions from './ModalActions'; -import ModalTabs from './ModalTabs'; - -const METHODS_TO_BIND = ['handleTabChange']; - -export default class Modal extends React.Component { - constructor() { - super(); - - this.domRefs = {}; - this.state = { - activeTabId: null, - }; - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - getActiveTabId() { - if (this.state.activeTabId) { - return this.state.activeTabId; - } - - return Object.keys(this.props.tabs)[0]; - } - - handleTabChange(tab) { - this.setState({activeTabId: tab.id}); - } - - setRef(id, ref) { - this.domRefs[id] = ref; - - if (this.props.onSetRef) { - this.props.onSetRef(id, ref); - } - } - - render() { - const contentWrapperClasses = classnames( - 'modal__content__wrapper', - `modal--align-${this.props.alignment}`, - `modal--size-${this.props.size}`, - { - 'modal--horizontal': this.props.orientation === 'horizontal', - 'modal--vertical': this.props.orientation === 'vertical', - 'modal--tabs-in-header': !this.props.tabsInBody, - 'modal--tabs-in-body': this.props.tabsInBody, - inverse: this.props.inverse, - }, - this.props.className, - ); - let modalBody = this.props.content; - const modalHeader = this.props.heading; - const headerClasses = classnames('modal__header', { - 'has-tabs': this.props.tabs, - }); - - let bodyTabs; - let footer; - let headerTabs; - - if (this.props.tabs) { - const activeTabId = this.getActiveTabId(); - const activeTab = this.props.tabs[activeTabId]; - const contentClasses = classnames('modal__content', activeTab.modalContentClasses); - - const ModalContentComponent = activeTab.content; - const modalContentData = activeTab.props; - - const tabs = ( - - ); - - if (this.props.tabsInBody) { - bodyTabs = tabs; - } else { - headerTabs = tabs; - } - - modalBody = [ - bodyTabs, -
    - -
    , - ]; - } - - if (this.props.actions) { - footer = ( -
    - -
    - ); - } - - return ( -
    -
    - {modalHeader} - {headerTabs} -
    -
    this.setRef('modal-body', ref)}> - {modalBody} - {footer} -
    -
    - ); - } -} - -Modal.defaultProps = { - alignment: 'left', - className: null, - inverse: true, - size: 'medium', - orientation: 'horizontal', - tabsInBody: false, -}; diff --git a/client/src/javascript/components/modals/Modal.tsx b/client/src/javascript/components/modals/Modal.tsx new file mode 100644 index 000000000..61e56b4ec --- /dev/null +++ b/client/src/javascript/components/modals/Modal.tsx @@ -0,0 +1,132 @@ +import classnames from 'classnames'; +import {FC, ReactNode, useState} from 'react'; + +import ModalActions from './ModalActions'; +import ModalTabs from './ModalTabs'; + +import type {ModalAction} from '../../stores/UIStore'; +import type {Tab} from './ModalTabs'; + +interface ModalProps { + heading: ReactNode; + content?: ReactNode; + className?: string | null; + alignment?: 'left' | 'center'; + size?: 'medium' | 'large'; + orientation?: 'horizontal' | 'vertical'; + tabsInBody?: boolean; + inverse?: boolean; + actions?: Array; + tabs?: Record; + initialTabId?: string; +} + +const Modal: FC = (props: ModalProps) => { + const { + alignment, + size, + orientation, + tabsInBody, + inverse, + initialTabId, + className, + content, + heading, + tabs, + actions, + } = props; + + const contentWrapperClasses = classnames( + 'modal__content__wrapper', + `modal--align-${alignment}`, + `modal--size-${size}`, + { + 'modal--horizontal': orientation === 'horizontal', + 'modal--vertical': orientation === 'vertical', + 'modal--tabs-in-header': !tabsInBody, + 'modal--tabs-in-body': tabsInBody, + inverse, + }, + className, + ); + let modalBody = content; + const modalHeader = heading; + const headerClasses = classnames('modal__header', { + 'has-tabs': tabs, + }); + + let bodyTabs; + let footer; + let headerTabs; + + const [activeTabId, setActiveTabId] = useState(initialTabId ?? Object.keys(tabs || {})[0]); + if (tabs) { + const activeTab = tabs[activeTabId]; + const contentClasses = classnames('modal__content', activeTab.modalContentClasses); + + const ModalContentComponent = activeTab.content as FC; + const modalContentData = activeTab.props; + + const modalTabs = ( + { + if (tab.id != null) { + setActiveTabId(tab.id); + } + }} + tabs={tabs} + /> + ); + + if (tabsInBody) { + bodyTabs = modalTabs; + } else { + headerTabs = modalTabs; + } + + modalBody = [ + bodyTabs, +
    + +
    , + ]; + } + + if (actions) { + footer = ( +
    + +
    + ); + } + + return ( +
    +
    + {modalHeader} + {headerTabs} +
    +
    + {modalBody} + {footer} +
    +
    + ); +}; + +Modal.defaultProps = { + alignment: 'left', + className: null, + inverse: true, + size: 'medium', + orientation: 'horizontal', + tabsInBody: false, + content: undefined, + actions: undefined, + tabs: undefined, + initialTabId: undefined, +}; + +export default Modal; diff --git a/client/src/javascript/components/modals/ModalActions.js b/client/src/javascript/components/modals/ModalActions.js deleted file mode 100644 index 2021a4c54..000000000 --- a/client/src/javascript/components/modals/ModalActions.js +++ /dev/null @@ -1,59 +0,0 @@ -import {Button, Checkbox} from 'flood-ui-kit'; -import classnames from 'classnames'; -import React from 'react'; - -import UIActions from '../../actions/UIActions'; - -export default class ModalActions extends React.Component { - getModalButtons(actions) { - const buttons = actions.map((action, index) => { - const classes = classnames('button', { - [action.supplementalClassName]: action.supplementalClassName, - }); - - if (action.type === 'checkbox') { - return ( - // eslint-disable-next-line react/no-array-index-key - - {action.content} - - ); - } - - return ( - - ); - }); - - return
    {buttons}
    ; - } - - getClickHandler(action) { - return event => { - if (action.clickHandler) { - action.clickHandler(event); - } - - if (action.triggerDismiss) { - UIActions.dismissModal(); - } - }; - } - - render() { - return
    {this.getModalButtons(this.props.actions)}
    ; - } -} - -ModalActions.defaultProps = { - alignment: 'left', -}; diff --git a/client/src/javascript/components/modals/ModalActions.tsx b/client/src/javascript/components/modals/ModalActions.tsx new file mode 100644 index 000000000..748f2d4b9 --- /dev/null +++ b/client/src/javascript/components/modals/ModalActions.tsx @@ -0,0 +1,65 @@ +import {FC} from 'react'; + +import {Button, Checkbox} from '../../ui'; +import UIActions from '../../actions/UIActions'; + +import type {ModalAction} from '../../stores/UIStore'; + +interface ModalActionsProps { + actions: Array; +} + +const ModalActions: FC = (props: ModalActionsProps) => { + const {actions} = props; + + const buttons = actions.map((action, index) => { + let dismissIfNeeded = () => { + // do nothing by default. + }; + + if (action.triggerDismiss) { + dismissIfNeeded = () => { + UIActions.dismissModal(); + }; + } + + if (action.type === 'checkbox') { + return ( + { + if (action.clickHandler != null) { + action.clickHandler(event); + } + dismissIfNeeded(); + }}> + {action.content} + + ); + } + + return ( + + ); + }); + + const buttonsGroup =
    {buttons}
    ; + + return
    {buttonsGroup}
    ; +}; + +export default ModalActions; diff --git a/client/src/javascript/components/modals/ModalFormSectionHeader.js b/client/src/javascript/components/modals/ModalFormSectionHeader.js deleted file mode 100644 index 823ff3f60..000000000 --- a/client/src/javascript/components/modals/ModalFormSectionHeader.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; - -class ModalFormSectionHeader extends React.PureComponent { - render() { - return

    {this.props.children}

    ; - } -} - -export default ModalFormSectionHeader; diff --git a/client/src/javascript/components/modals/ModalFormSectionHeader.tsx b/client/src/javascript/components/modals/ModalFormSectionHeader.tsx new file mode 100644 index 000000000..ff960a280 --- /dev/null +++ b/client/src/javascript/components/modals/ModalFormSectionHeader.tsx @@ -0,0 +1,11 @@ +import {FC, ReactNode} from 'react'; + +interface ModalFormSectionHeaderProps { + children: ReactNode; +} + +const ModalFormSectionHeader: FC = ({children}: ModalFormSectionHeaderProps) => ( +

    {children}

    +); + +export default ModalFormSectionHeader; diff --git a/client/src/javascript/components/modals/ModalTabs.js b/client/src/javascript/components/modals/ModalTabs.js deleted file mode 100644 index 343aaa8e8..000000000 --- a/client/src/javascript/components/modals/ModalTabs.js +++ /dev/null @@ -1,33 +0,0 @@ -import classnames from 'classnames'; -import React from 'react'; - -export default class ModalTabs extends React.Component { - handleTabClick(tab) { - if (this.props.onTabChange) { - this.props.onTabChange(tab); - } - } - - render() { - const tabs = Object.keys(this.props.tabs).map(tabId => { - const currentTab = this.props.tabs[tabId]; - - currentTab.id = tabId; - - const classes = classnames('modal__tab', { - 'is-active': tabId === this.props.activeTabId, - }); - - return ( -
  • - {currentTab.label} -
  • - ); - }); - return
      {tabs}
    ; - } -} - -ModalTabs.defaultProps = { - tabs: [], -}; diff --git a/client/src/javascript/components/modals/ModalTabs.tsx b/client/src/javascript/components/modals/ModalTabs.tsx new file mode 100644 index 000000000..310d89874 --- /dev/null +++ b/client/src/javascript/components/modals/ModalTabs.tsx @@ -0,0 +1,51 @@ +import classnames from 'classnames'; +import {FC, ReactNode, ReactNodeArray} from 'react'; + +export interface Tab { + id?: string; + label: ReactNode; + content: ReactNode | FC; + props?: Record; + modalContentClasses?: string; +} + +interface ModalTabsProps { + activeTabId: string | null; + tabs?: Record; + onTabChange: (tab: Tab) => void; +} + +const ModalTabs: FC = (props: ModalTabsProps) => { + const {activeTabId, tabs = {}, onTabChange} = props; + + const tabNodes: ReactNodeArray = Object.keys(tabs).map((tabId) => { + const currentTab = tabs[tabId]; + + currentTab.id = tabId; + + const classes = classnames('modal__tab', { + 'is-active': tabId === activeTabId, + }); + + return ( +
  • { + if (onTabChange) { + onTabChange(currentTab); + } + }}> + {currentTab.label} +
  • + ); + }); + + return
      {tabNodes}
    ; +}; + +ModalTabs.defaultProps = { + tabs: {}, +}; + +export default ModalTabs; diff --git a/client/src/javascript/components/modals/Modals.js b/client/src/javascript/components/modals/Modals.js deleted file mode 100644 index 63c4045e4..000000000 --- a/client/src/javascript/components/modals/Modals.js +++ /dev/null @@ -1,100 +0,0 @@ -import _ from 'lodash'; -import {CSSTransition, TransitionGroup} from 'react-transition-group'; -import React from 'react'; - -import AddTorrentsModal from './add-torrents-modal/AddTorrentsModal'; -import ConfirmModal from './confirm-modal/ConfirmModal'; -import connectStores from '../../util/connectStores'; -import EventTypes from '../../constants/EventTypes'; -import FeedsModal from './feeds-modal/FeedsModal'; -import MoveTorrentsModal from './move-torrents-modal/MoveTorrentsModal'; -import RemoveTorrentsModal from './remove-torrents-modal/RemoveTorrentsModal'; -import SetTagsModal from './set-tags-modal/SetTagsModal'; -import SettingsModal from './settings-modal/SettingsModal'; -import TorrentDetailsModal from './torrent-details-modal/TorrentDetailsModal'; -import UIActions from '../../actions/UIActions'; -import UIStore from '../../stores/UIStore'; - -class Modals extends React.Component { - constructor() { - super(); - - this.modals = { - 'add-torrents': AddTorrentsModal, - confirm: ConfirmModal, - feeds: FeedsModal, - 'move-torrents': MoveTorrentsModal, - 'remove-torrents': RemoveTorrentsModal, - 'set-taxonomy': SetTagsModal, - settings: SettingsModal, - 'torrent-details': TorrentDetailsModal, - }; - - this.handleKeyPress = _.throttle(this.handleKeyPress, 1000); - } - - componentDidMount() { - window.addEventListener('keydown', this.handleKeyPress); - } - - componentWillUnmount() { - window.removeEventListener('keydown', this.handleKeyPress); - } - - dismissModal() { - UIActions.dismissModal(); - } - - getModal() { - const ActiveModal = this.modals[this.props.activeModal.id]; - - return ; - } - - handleKeyPress = event => { - if (this.props.activeModal != null && event.keyCode === 27) { - this.dismissModal(); - } - }; - - handleModalClick(event) { - event.stopPropagation(); - } - - handleOverlayClick = () => { - this.dismissModal(); - }; - - render() { - let modal; - - if (this.props.activeModal != null) { - modal = ( - -
    -
    - {this.getModal()} -
    - - ); - } - - return {modal}; - } -} - -const ConnectedModals = connectStores(Modals, () => { - return [ - { - store: UIStore, - event: EventTypes.UI_MODAL_CHANGE, - getValue: ({store}) => { - return { - activeModal: store.getActiveModal(), - }; - }, - }, - ]; -}); - -export default ConnectedModals; diff --git a/client/src/javascript/components/modals/Modals.tsx b/client/src/javascript/components/modals/Modals.tsx new file mode 100644 index 000000000..1425477ef --- /dev/null +++ b/client/src/javascript/components/modals/Modals.tsx @@ -0,0 +1,73 @@ +import {CSSTransition, TransitionGroup} from 'react-transition-group'; +import {FC} from 'react'; +import {observer} from 'mobx-react'; +import {useKeyPressEvent} from 'react-use'; + +import AddTorrentsModal from './add-torrents-modal/AddTorrentsModal'; +import ConfirmModal from './confirm-modal/ConfirmModal'; +import FeedsModal from './feeds-modal/FeedsModal'; +import GenerateMagnetModal from './generate-magnet-modal/GenerateMagnetModal'; +import MoveTorrentsModal from './move-torrents-modal/MoveTorrentsModal'; +import RemoveTorrentsModal from './remove-torrents-modal/RemoveTorrentsModal'; +import SetTagsModal from './set-tags-modal/SetTagsModal'; +import SetTrackersModal from './set-trackers-modal/SetTrackersModal'; +import SettingsModal from './settings-modal/SettingsModal'; +import TorrentDetailsModal from './torrent-details-modal/TorrentDetailsModal'; +import UIActions from '../../actions/UIActions'; +import UIStore from '../../stores/UIStore'; + +import type {Modal} from '../../stores/UIStore'; + +const createModal = (id: Modal['id']): React.ReactNode => { + switch (id) { + case 'add-torrents': + return ; + case 'confirm': + return ; + case 'feeds': + return ; + case 'generate-magnet': + return ; + case 'move-torrents': + return ; + case 'remove-torrents': + return ; + case 'set-taxonomy': + return ; + case 'set-trackers': + return ; + case 'settings': + return ; + case 'torrent-details': + return ; + default: + return null; + } +}; + +const Modals: FC = observer(() => { + const {id} = UIStore.activeModal || {}; + + useKeyPressEvent('Escape', () => UIActions.dismissModal()); + + let modal; + if (id != null) { + modal = ( + +
    +
    { + UIActions.dismissModal(); + }} + /> + {createModal(id)} +
    + + ); + } + + return {modal}; +}); + +export default Modals; diff --git a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsActions.js b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsActions.js deleted file mode 100644 index 3c357cd67..000000000 --- a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsActions.js +++ /dev/null @@ -1,50 +0,0 @@ -import {injectIntl} from 'react-intl'; -import React, {PureComponent} from 'react'; - -import ModalActions from '../ModalActions'; -import SettingsStore from '../../../stores/SettingsStore'; - -class AddTorrentsActions extends PureComponent { - getActions() { - const startTorrentsOnLoad = SettingsStore.getFloodSettings('startTorrentsOnLoad'); - return [ - { - checked: startTorrentsOnLoad === 'true' || startTorrentsOnLoad === true, - clickHandler: this.handleStartTorrentsToggle, - content: this.props.intl.formatMessage({ - id: 'torrents.add.start.label', - defaultMessage: 'Start Torrent', - }), - id: 'start', - triggerDismiss: false, - type: 'checkbox', - }, - { - clickHandler: null, - content: this.props.intl.formatMessage({ - id: 'button.cancel', - defaultMessage: 'Cancel', - }), - triggerDismiss: true, - type: 'tertiary', - }, - { - clickHandler: this.props.onAddTorrentsClick, - content: this.props.intl.formatMessage({ - id: 'torrents.add.button.add', - defaultMessage: 'Add Torrent', - }), - isLoading: this.props.isAddingTorrents, - submit: true, - triggerDismiss: false, - type: 'primary', - }, - ]; - } - - render() { - return ; - } -} - -export default injectIntl(AddTorrentsActions); diff --git a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsActions.tsx b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsActions.tsx new file mode 100644 index 000000000..5c29c21dc --- /dev/null +++ b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsActions.tsx @@ -0,0 +1,53 @@ +import {FC} from 'react'; +import {useIntl} from 'react-intl'; + +import ModalActions from '../ModalActions'; +import SettingStore from '../../../stores/SettingStore'; + +interface AddTorrentsActionsProps { + isAddingTorrents: boolean; + onAddTorrentsClick: (event: React.MouseEvent) => void; +} + +const AddTorrentsActions: FC = ({ + isAddingTorrents, + onAddTorrentsClick, +}: AddTorrentsActionsProps) => { + const intl = useIntl(); + return ( + + ); +}; + +export default AddTorrentsActions; diff --git a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByCreation.tsx b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByCreation.tsx new file mode 100644 index 000000000..ffed151a0 --- /dev/null +++ b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByCreation.tsx @@ -0,0 +1,135 @@ +import {FC, useRef, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import AddTorrentsActions from './AddTorrentsActions'; +import {Checkbox, Form, FormRow, Textbox} from '../../../ui'; +import FilesystemBrowserTextbox from '../../general/form-elements/FilesystemBrowserTextbox'; +import {saveAddTorrentsUserPreferences} from '../../../util/userPreferences'; +import TagSelect from '../../general/form-elements/TagSelect'; +import TextboxRepeater, {getTextArray} from '../../general/form-elements/TextboxRepeater'; +import TorrentActions from '../../../actions/TorrentActions'; +import UIStore from '../../../stores/UIStore'; + +type AddTorrentsByCreationFormData = { + [trackers: string]: string; +} & { + name: string; + sourcePath: string; + comment: string; + infoSource: string; + isPrivate: boolean; + start: boolean; + tags: string; +}; + +const AddTorrentsByCreation: FC = () => { + const formRef = useRef
    (null); + const intl = useIntl(); + const [isCreatingTorrents, setIsCreatingTorrents] = useState(false); + + return ( + + + + + + + + + + + + + + + {intl.formatMessage({id: 'torrents.create.is.private.label'})} + + + + + + { + if (formRef.current == null) { + return; + } + + const formData = formRef.current.getFormData() as Partial; + setIsCreatingTorrents(true); + + if (formData.sourcePath == null) { + return; + } + + TorrentActions.createTorrent({ + name: formData.name, + sourcePath: formData.sourcePath, + trackers: getTextArray(formData, 'trackers'), + comment: formData.comment, + infoSource: formData.infoSource, + isPrivate: formData.isPrivate || false, + start: formData.start || false, + tags: formData.tags != null ? formData.tags.split(',') : undefined, + }).then(() => { + UIStore.dismissModal(); + }); + + saveAddTorrentsUserPreferences({ + start: formData.start, + destination: formData.sourcePath, + tab: 'by-creation', + }); + }} + isAddingTorrents={isCreatingTorrents} + /> + + ); +}; + +export default AddTorrentsByCreation; diff --git a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByFile.js b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByFile.js deleted file mode 100644 index f9336cb68..000000000 --- a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByFile.js +++ /dev/null @@ -1,153 +0,0 @@ -import {FormattedMessage, injectIntl} from 'react-intl'; -import {Form, FormRow, FormRowItem, Textbox} from 'flood-ui-kit'; -import Dropzone from 'react-dropzone'; -import React from 'react'; - -import AddTorrentsActions from './AddTorrentsActions'; -import Close from '../../icons/Close'; -import File from '../../icons/File'; -import Files from '../../icons/Files'; -import SettingsStore from '../../../stores/SettingsStore'; -import TorrentActions from '../../../actions/TorrentActions'; -import TorrentDestination from '../../general/filesystem/TorrentDestination'; - -class AddTorrentsByFile extends React.Component { - state = { - errors: {}, - isAddingTorrents: false, - files: [], - tags: '', - }; - - formRef = null; - - getFileDropzone() { - let fileContent = null; - - if (this.state.files.length > 0) { - const files = this.state.files.map((file, index) => ( -
  • - - - - {file.name} - this.handleFileRemove(index)}> - - -
  • - )); - - fileContent = ( -
      - {files} -
    - ); - } - - return ( - - - {fileContent} - -
    -
    - -
    - {' '} - - - - . -
    -
    -
    - ); - } - - handleFileDrop = files => { - const nextErrorsState = this.state.errors; - - if (nextErrorsState.files != null) { - delete nextErrorsState.files; - } - - this.setState(state => ({errors: nextErrorsState, files: state.files.concat(files)})); - }; - - handleFileRemove = fileIndex => { - const {files} = this.state; - files.splice(fileIndex, 1); - this.setState({files}); - }; - - handleFilesClick(event) { - event.stopPropagation(); - } - - handleAddTorrents = () => { - const formData = this.formRef.getFormData(); - this.setState({isAddingTorrents: true}); - - const fileData = new FormData(); - const {destination, start, tags, useBasePath} = formData; - - this.state.files.forEach(file => { - fileData.append('torrents', file); - }); - - tags.split(',').forEach(tag => { - fileData.append('tags', tag); - }); - - fileData.append('destination', destination); - fileData.append('isBasePath', useBasePath); - fileData.append('start', start); - - TorrentActions.addTorrentsByFiles(fileData, destination); - SettingsStore.updateOptimisticallyOnly({id: 'startTorrentsOnLoad', data: start}); - }; - - render() { - return ( -
    { - this.formRef = ref; - }}> - {this.getFileDropzone()} - - - - - - - ); - } -} - -export default injectIntl(AddTorrentsByFile, {withRef: true}); diff --git a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByFile.tsx b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByFile.tsx new file mode 100644 index 000000000..1588653a8 --- /dev/null +++ b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByFile.tsx @@ -0,0 +1,115 @@ +import {FC, useRef, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import AddTorrentsActions from './AddTorrentsActions'; +import FileDropzone from '../../general/form-elements/FileDropzone'; +import FilesystemBrowserTextbox from '../../general/form-elements/FilesystemBrowserTextbox'; +import {Form, FormRow} from '../../../ui'; +import {saveAddTorrentsUserPreferences} from '../../../util/userPreferences'; +import SettingStore from '../../../stores/SettingStore'; +import TagSelect from '../../general/form-elements/TagSelect'; +import TorrentActions from '../../../actions/TorrentActions'; +import UIStore from '../../../stores/UIStore'; + +import type {ProcessedFiles} from '../../general/form-elements/FileDropzone'; + +interface AddTorrentsByFileFormData { + destination: string; + start: boolean; + tags: string; + isBasePath: boolean; + isCompleted: boolean; +} + +const AddTorrentsByFile: FC = () => { + const filesRef = useRef([]); + const formRef = useRef
    (null); + const textboxRef = useRef(null); + const [isAddingTorrents, setIsAddingTorrents] = useState(false); + + const intl = useIntl(); + + return ( + + + { + filesRef.current = files; + }} + /> + + + { + if (textboxRef.current != null) { + const suggestedPath = SettingStore.floodSettings.torrentDestinations?.[tags[0]]; + if (typeof suggestedPath === 'string' && textboxRef.current != null) { + textboxRef.current.value = suggestedPath; + textboxRef.current.dispatchEvent(new Event('input', {bubbles: true})); + } + } + }} + /> + + + { + if (formRef.current == null) { + return; + } + + const formData = formRef.current?.getFormData(); + setIsAddingTorrents(true); + + const {destination, start, tags, isBasePath, isCompleted} = formData as Partial; + + const filesData: Array = []; + filesRef.current.forEach((file) => { + filesData.push(file.data); + }); + + if (filesData.length === 0 || destination == null) { + setIsAddingTorrents(false); + return; + } + + const tagsArray = tags != null ? tags.split(',').filter((tag) => tag.length > 0) : undefined; + + TorrentActions.addTorrentsByFiles({ + files: filesData as [string, ...string[]], + destination, + tags: tagsArray, + isBasePath, + isCompleted, + start, + }).then(() => { + UIStore.dismissModal(); + }); + + saveAddTorrentsUserPreferences({ + start, + destination, + tags: tagsArray, + tab: 'by-file', + }); + }} + isAddingTorrents={isAddingTorrents} + /> + + ); +}; + +export default AddTorrentsByFile; diff --git a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByURL.js b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByURL.js deleted file mode 100644 index d1e6890bc..000000000 --- a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByURL.js +++ /dev/null @@ -1,104 +0,0 @@ -import {Form, FormRow, Textbox} from 'flood-ui-kit'; -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import AddTorrentsActions from './AddTorrentsActions'; - -import SettingsStore from '../../../stores/SettingsStore'; -import TextboxRepeater from '../../general/form-elements/TextboxRepeater'; -import TorrentActions from '../../../actions/TorrentActions'; -import TorrentDestination from '../../general/filesystem/TorrentDestination'; -import UIStore from '../../../stores/UIStore'; - -class AddTorrentsByURL extends React.Component { - formRef = null; - - constructor() { - super(); - - const activeModal = UIStore.getActiveModal(); - const initialUrls = activeModal ? activeModal.torrents : null; - - this.state = { - isAddingTorrents: false, - tags: '', - urlTextboxes: initialUrls || [{id: 0, value: ''}], - }; - } - - getURLsFromForm() { - const formData = this.formRef.getFormData(); - return Object.keys(formData).reduce((accumulator, formItemKey) => { - if (/^urls/.test(formItemKey)) { - accumulator.push(formData[formItemKey]); - } - - return accumulator; - }, []); - } - - handleAddTorrents = () => { - const formData = this.formRef.getFormData(); - this.setState({isAddingTorrents: true}); - - TorrentActions.addTorrentsByUrls({ - urls: this.getURLsFromForm(), - destination: formData.destination, - isBasePath: formData.useBasePath, - start: formData.start, - tags: formData.tags.split(','), - }); - - SettingsStore.updateOptimisticallyOnly({ - id: 'startTorrentsOnLoad', - data: formData.start, - }); - }; - - render() { - return ( -
    { - this.formRef = ref; - }}> - - - - - - - - ); - } -} - -export default injectIntl(AddTorrentsByURL, {withRef: true}); diff --git a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByURL.tsx b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByURL.tsx new file mode 100644 index 000000000..070e6e889 --- /dev/null +++ b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsByURL.tsx @@ -0,0 +1,156 @@ +import {FC, useRef, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import AddTorrentsActions from './AddTorrentsActions'; +import ConfigStore from '../../../stores/ConfigStore'; +import FilesystemBrowserTextbox from '../../general/form-elements/FilesystemBrowserTextbox'; +import {Form, FormRow} from '../../../ui'; +import {saveAddTorrentsUserPreferences} from '../../../util/userPreferences'; +import SettingStore from '../../../stores/SettingStore'; +import TagSelect from '../../general/form-elements/TagSelect'; +import TextboxRepeater, {getTextArray} from '../../general/form-elements/TextboxRepeater'; +import TorrentActions from '../../../actions/TorrentActions'; +import UIStore from '../../../stores/UIStore'; + +type AddTorrentsByURLFormData = { + [urls: string]: string; +} & { + [cookies: string]: string; +} & { + destination: string; + isBasePath: boolean; + isCompleted: boolean; + start: boolean; + tags: string; +}; + +const AddTorrentsByURL: FC = () => { + const formRef = useRef
    (null); + const textboxRef = useRef(null); + const [isAddingTorrents, setIsAddingTorrents] = useState(false); + + const intl = useIntl(); + + return ( + + + {intl.formatMessage({ + id: 'torrents.add.torrents.label', + })} + { + if (typeof navigator.registerProtocolHandler === 'function') { + navigator.registerProtocolHandler( + 'magnet', + `${ConfigStore.baseURI}?action=add-urls&url=%s`, + 'Magnet -> Flood', + ); + } + }}> + {intl.formatMessage({ + id: 'torrents.add.tab.url.register.magnet.handler', + })} + +
    + } + placeholder={intl.formatMessage({ + id: 'torrents.add.tab.url.input.placeholder', + })} + defaultValues={ + (UIStore.activeModal?.id === 'add-torrents' && UIStore.activeModal?.initialURLs) || [{id: 0, value: ''}] + } + /> + + + { + if (textboxRef.current != null) { + const suggestedPath = SettingStore.floodSettings.torrentDestinations?.[tags[0]]; + if (typeof suggestedPath === 'string' && textboxRef.current != null) { + textboxRef.current.value = suggestedPath; + textboxRef.current.dispatchEvent(new Event('input', {bubbles: true})); + } + } + }} + /> + + + { + if (formRef.current == null) { + return; + } + + const formData = formRef.current.getFormData() as Partial; + setIsAddingTorrents(true); + + const urls = getTextArray(formData, 'urls').filter((url) => url !== ''); + + if (urls.length === 0 || formData.destination == null) { + setIsAddingTorrents(false); + return; + } + + const cookies = getTextArray(formData, 'cookies'); + + // TODO: handle multiple domain names + const firstDomain = urls[0].startsWith('http') && urls[0].split('/')[2]; + const processedCookies = firstDomain + ? { + [firstDomain]: cookies, + } + : undefined; + + const tags = formData.tags != null ? formData.tags.split(',').filter((tag) => tag.length > 0) : undefined; + + TorrentActions.addTorrentsByUrls({ + urls: urls as [string, ...string[]], + cookies: processedCookies, + destination: formData.destination, + isBasePath: formData.isBasePath, + isCompleted: formData.isCompleted, + start: formData.start, + tags, + }).then(() => { + UIStore.dismissModal(); + }); + + saveAddTorrentsUserPreferences({ + start: formData.start, + destination: formData.destination, + tags, + tab: 'by-url', + }); + }} + isAddingTorrents={isAddingTorrents} + /> + + ); +}; + +export default AddTorrentsByURL; diff --git a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsModal.js b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsModal.js deleted file mode 100644 index ead2fa9d9..000000000 --- a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsModal.js +++ /dev/null @@ -1,45 +0,0 @@ -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import AddTorrentsByFile from './AddTorrentsByFile'; -import AddTorrentsByURL from './AddTorrentsByURL'; -import Modal from '../Modal'; -import UIActions from '../../../actions/UIActions'; - -class AddTorrents extends React.Component { - dismissModal() { - UIActions.dismissModal(); - } - - render() { - const tabs = { - 'by-url': { - content: AddTorrentsByURL, - label: this.props.intl.formatMessage({ - id: 'torrents.add.tab.url.title', - defaultMessage: 'By URL', - }), - }, - 'by-file': { - content: AddTorrentsByFile, - label: this.props.intl.formatMessage({ - id: 'torrents.add.tab.file.title', - defaultMessage: 'By File', - }), - }, - }; - - return ( - - ); - } -} - -export default injectIntl(AddTorrents); diff --git a/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsModal.tsx b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsModal.tsx new file mode 100644 index 000000000..8c70471f8 --- /dev/null +++ b/client/src/javascript/components/modals/add-torrents-modal/AddTorrentsModal.tsx @@ -0,0 +1,55 @@ +import {FC} from 'react'; +import {useIntl} from 'react-intl'; + +import AddTorrentsByCreation from './AddTorrentsByCreation'; +import AddTorrentsByFile from './AddTorrentsByFile'; +import AddTorrentsByURL from './AddTorrentsByURL'; +import Modal from '../Modal'; +import SettingStore from '../../../stores/SettingStore'; +import UIStore from '../../../stores/UIStore'; + +const AddTorrentsModal: FC = () => { + const intl = useIntl(); + + const tabs = { + 'by-url': { + content: AddTorrentsByURL, + label: intl.formatMessage({ + id: 'torrents.add.tab.url.title', + }), + }, + 'by-file': { + content: AddTorrentsByFile, + label: intl.formatMessage({ + id: 'torrents.add.tab.file.title', + }), + }, + 'by-creation': { + content: AddTorrentsByCreation, + label: intl.formatMessage({ + id: 'torrents.add.tab.create.title', + }), + }, + }; + + if (UIStore.activeModal?.id !== 'add-torrents') { + return null; + } + + let initialTabId: keyof typeof tabs = 'by-url'; + if (!UIStore.activeModal.initialURLs?.length) { + initialTabId = SettingStore.floodSettings.UITorrentsAddTab ?? initialTabId; + } + + return ( + + ); +}; + +export default AddTorrentsModal; diff --git a/client/src/javascript/components/modals/confirm-modal/ConfirmModal.js b/client/src/javascript/components/modals/confirm-modal/ConfirmModal.js deleted file mode 100644 index d9de240d8..000000000 --- a/client/src/javascript/components/modals/confirm-modal/ConfirmModal.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -import Modal from '../Modal'; - -export default class ConfirmModal extends React.Component { - getContent() { - return
    {this.props.options.content}
    ; - } - - render() { - return ( - - ); - } -} diff --git a/client/src/javascript/components/modals/confirm-modal/ConfirmModal.tsx b/client/src/javascript/components/modals/confirm-modal/ConfirmModal.tsx new file mode 100644 index 000000000..4ae8a3dd5 --- /dev/null +++ b/client/src/javascript/components/modals/confirm-modal/ConfirmModal.tsx @@ -0,0 +1,17 @@ +import {FC} from 'react'; +import {observer} from 'mobx-react'; + +import Modal from '../Modal'; +import UIStore from '../../../stores/UIStore'; + +const ConfirmModal: FC = observer(() => { + if (UIStore.activeModal?.id !== 'confirm') { + return null; + } + + const {actions, content, heading} = UIStore.activeModal || {}; + + return {content}
    } heading={heading} />; +}); + +export default ConfirmModal; diff --git a/client/src/javascript/components/modals/feeds-modal/DownloadRulesTab.js b/client/src/javascript/components/modals/feeds-modal/DownloadRulesTab.js deleted file mode 100644 index 57e455562..000000000 --- a/client/src/javascript/components/modals/feeds-modal/DownloadRulesTab.js +++ /dev/null @@ -1,480 +0,0 @@ -import _ from 'lodash'; -import { - Button, - Checkbox, - Form, - FormElementAddon, - FormError, - FormRow, - FormRowGroup, - FormRowItem, - Select, - SelectItem, - Textbox, -} from 'flood-ui-kit'; -import {defineMessages, FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import connectStores from '../../../util/connectStores'; -import Edit from '../../icons/Edit'; -import Checkmark from '../../icons/Checkmark'; -import Close from '../../icons/Close'; -import EventTypes from '../../../constants/EventTypes'; -import FeedMonitorStore from '../../../stores/FeedMonitorStore'; -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import TorrentDestination from '../../general/filesystem/TorrentDestination'; -import * as validators from '../../../util/validators'; - -const MESSAGES = defineMessages({ - mustSpecifyDestination: { - id: 'feeds.validation.must.specify.destination', - defaultMessage: 'You must specify a destination.', - }, - mustSelectFeed: { - id: 'feeds.validation.must.select.feed', - defaultMessage: 'You must select a feed.', - }, - mustSpecifyLabel: { - id: 'feeds.validation.must.specify.label', - defaultMessage: 'You must specify a label.', - }, - invalidRegularExpression: { - id: 'feeds.validation.invalid.regular.expression', - defaultMessage: 'Invalid regular expression.', - }, - url: { - id: 'feeds.url', - defaultMessage: 'URL', - }, - label: { - id: 'feeds.label', - defaultMessage: 'Label', - }, - regEx: { - id: 'feeds.regEx', - defaultMessage: 'RegEx', - }, - tags: { - id: 'feeds.tags', - defaultMessage: 'Tags', - }, - check: { - id: 'feeds.check', - defaultMessage: 'Test Match Pattern', - }, -}); - -const defaultRule = { - label: '', - feedID: '', - match: '', - exclude: '', - tags: [], - destination: '', - startOnLoad: false, -}; - -class DownloadRulesTab extends React.Component { - state = { - errors: {}, - currentlyEditingRule: null, - doesPatternMatchTest: false, - }; - - validatedFields = { - destination: { - isValid: validators.isNotEmpty, - error: this.props.intl.formatMessage(MESSAGES.mustSpecifyDestination), - }, - feedID: { - isValid: validators.isNotEmpty, - error: this.props.intl.formatMessage(MESSAGES.mustSelectFeed), - }, - label: { - isValid: validators.isNotEmpty, - error: this.props.intl.formatMessage(MESSAGES.mustSpecifyLabel), - }, - match: { - isValid: value => validators.isNotEmpty(value) && validators.isRegExValid(value), - error: this.props.intl.formatMessage(MESSAGES.invalidRegularExpression), - }, - exclude: { - isValid: value => { - if (validators.isNotEmpty(value)) { - return validators.isRegExValid(value); - } - - return true; - }, - error: this.props.intl.formatMessage(MESSAGES.invalidRegularExpression), - }, - }; - - formRef = null; - - checkFieldValidity = _.throttle((fieldName, fieldValue) => { - const {errors} = this.state; - - if (errors[fieldName] && this.validatedFields[fieldName].isValid(fieldValue)) { - delete errors[fieldName]; - this.setState({errors}); - } - }, 150); - - checkMatchingPattern(match, exclude, check) { - let doesPatternMatchTest = false; - - if (validators.isNotEmpty(check) && validators.isRegExValid(match) && validators.isRegExValid(exclude)) { - const isMatched = new RegExp(match, 'gi').test(check); - const isExcluded = exclude !== '' && new RegExp(exclude, 'gi').test(check); - doesPatternMatchTest = isMatched && !isExcluded; - } - - this.setState({doesPatternMatchTest}); - } - - getAmendedFormData() { - const formData = this.formRef.getFormData(); - delete formData.check; - - return Object.assign({}, formData, { - field: 'title', - tags: formData.tags.split(','), - }); - } - - getAvailableFeedsOptions() { - const {feeds} = this.props; - - if (feeds.length === 0) { - return [ - - - - - , - ]; - } - - return feeds.reduce( - (feedOptions, feed) => - feedOptions.concat( - - {feed.label} - , - ), - [ - - - - - , - ], - ); - } - - getModifyRuleForm(rule) { - const {doesPatternMatchTest, currentlyEditingRule} = this.state; - - return ( - - - - - - - - - - {doesPatternMatchTest && ( - - - - )} - - - - - - - - - - - - - - - - - - ); - } - - getRulesListItem(rule) { - const matchedCount = rule.count || 0; - let excludeNode = null; - let tags = null; - - if (rule.exclude) { - excludeNode = ( -
  • - {rule.exclude} -
  • - ); - } - - if (rule.tags && rule.tags.length > 0) { - const tagNodes = rule.tags.map(tag => ( - - {tag} - - )); - - tags = ( -
  • - {tagNodes} -
  • - ); - } - - return ( -
  • -
    -
      -
    • - {rule.label} -
    • -
    • - -
    • - {rule === this.state.currentlyEditingRule && ( -
    • - Modifying -
    • - )} -
    -
      -
    • - {rule.match} -
    • - {excludeNode} - {tags} -
    -
    - this.handleModifyRuleClick(rule)}> - - - this.handleRemoveRuleClick(rule)}> - - -
  • - ); - } - - getRulesList() { - const {rules} = this.props; - - if (rules.length === 0) { - return ( -
      -
    • - -
    • -
    - ); - } - - const rulesList = rules.map(rule => this.getRulesListItem(rule)); - - return
      {rulesList}
    ; - } - - handleFormChange = ({event, formData}) => { - this.checkFieldValidity(event.target.name, formData[event.target.name]); - this.checkMatchingPattern(formData.match, formData.exclude, formData.check); - }; - - handleFormSubmit = () => { - const {errors, isValid} = this.validateForm(); - - if (!isValid) { - this.setState({errors}); - } else { - const currentRule = this.state.currentlyEditingRule; - const formData = this.getAmendedFormData(); - - if (currentRule !== null && currentRule !== defaultRule) { - FeedMonitorStore.removeRule(currentRule._id); - } - FeedMonitorStore.addRule(formData); - this.formRef.resetForm(); - this.setState({currentlyEditingRule: null}); - } - }; - - handleRemoveRuleClick(rule) { - FeedMonitorStore.removeRule(rule._id); - - if (rule === this.state.currentlyEditingRule) { - this.setState({currentlyEditingRule: null}); - } - } - - handleAddRuleClick = () => { - this.setState({currentlyEditingRule: defaultRule}); - }; - - handleModifyRuleClick(rule) { - this.setState({currentlyEditingRule: rule}); - } - - validateForm() { - const formData = this.getAmendedFormData(); - - const errors = Object.keys(this.validatedFields).reduce((accumulator, fieldName) => { - const fieldValue = formData[fieldName]; - - if (!this.validatedFields[fieldName].isValid(fieldValue)) { - accumulator[fieldName] = this.validatedFields[fieldName].error; - } - - return accumulator; - }, {}); - - return {errors, isValid: !Object.keys(errors).length}; - } - - render() { - const errors = Object.keys(this.state.errors).map(errorID => ( - - {this.state.errors[errorID]} - - )); - - return ( -
    { - this.formRef = ref; - }}> - - - - {errors} - - {this.getRulesList()} - - {this.state.currentlyEditingRule ? ( - this.getModifyRuleForm(this.state.currentlyEditingRule) - ) : ( - - - - - )} -
    - ); - } -} - -const ConnectedDownloadRulesTab = connectStores(injectIntl(DownloadRulesTab), () => { - return [ - { - store: FeedMonitorStore, - event: EventTypes.SETTINGS_FEED_MONITORS_FETCH_SUCCESS, - getValue: ({store}) => { - return { - feeds: store.getFeeds(), - rules: store.getRules(), - }; - }, - }, - ]; -}); - -export default ConnectedDownloadRulesTab; diff --git a/client/src/javascript/components/modals/feeds-modal/DownloadRulesTab.tsx b/client/src/javascript/components/modals/feeds-modal/DownloadRulesTab.tsx new file mode 100644 index 000000000..449701b85 --- /dev/null +++ b/client/src/javascript/components/modals/feeds-modal/DownloadRulesTab.tsx @@ -0,0 +1,580 @@ +import {defineMessages, FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; +import {observer} from 'mobx-react'; +import throttle from 'lodash/throttle'; +import * as React from 'react'; + +import type {AddRuleOptions} from '@shared/types/api/feed-monitor'; +import type {Rule} from '@shared/types/Feed'; + +import { + Button, + Checkbox, + Form, + FormElementAddon, + FormError, + FormRow, + FormRowGroup, + FormRowItem, + Select, + SelectItem, + Textbox, +} from '../../../ui'; +import Checkmark from '../../icons/Checkmark'; +import Close from '../../icons/Close'; +import Edit from '../../icons/Edit'; +import FeedActions from '../../../actions/FeedActions'; +import FeedStore from '../../../stores/FeedStore'; +import FilesystemBrowserTextbox from '../../general/form-elements/FilesystemBrowserTextbox'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; +import TagSelect from '../../general/form-elements/TagSelect'; +import * as validators from '../../../util/validators'; + +type ValidatedFields = 'destination' | 'feedID' | 'label' | 'match' | 'exclude'; + +interface RuleFormData extends Omit { + check: string; + feedID: string; + tags: string; +} + +interface DownloadRulesTabStates { + errors?: { + [field in ValidatedFields]?: string; + }; + isSubmitting: boolean; + isFormChanged: boolean; + currentlyEditingRule: Partial | null; + doesPatternMatchTest: boolean; +} + +const MESSAGES = defineMessages({ + mustSpecifyDestination: { + id: 'feeds.validation.must.specify.destination', + }, + mustSelectFeed: { + id: 'feeds.validation.must.select.feed', + }, + mustSpecifyLabel: { + id: 'feeds.validation.must.specify.label', + }, + invalidRegularExpression: { + id: 'feeds.validation.invalid.regular.expression', + }, + url: { + id: 'feeds.url', + }, + label: { + id: 'feeds.label', + }, + regEx: { + id: 'feeds.regEx', + }, + tags: { + id: 'feeds.tags', + }, + check: { + id: 'feeds.check', + }, +}); + +const defaultRule = { + label: '', + feedIDs: [''], + match: '', + exclude: '', + tags: [], + destination: '', + startOnLoad: false, +}; + +@observer +class DownloadRulesTab extends React.Component { + formRef: Form | null = null; + + validatedFields = { + destination: { + isValid: validators.isNotEmpty, + error: this.props.intl.formatMessage(MESSAGES.mustSpecifyDestination), + }, + feedID: { + isValid: validators.isNotEmpty, + error: this.props.intl.formatMessage(MESSAGES.mustSelectFeed), + }, + label: { + isValid: validators.isNotEmpty, + error: this.props.intl.formatMessage(MESSAGES.mustSpecifyLabel), + }, + match: { + isValid: (value: string) => validators.isNotEmpty(value) && validators.isRegExValid(value), + error: this.props.intl.formatMessage(MESSAGES.invalidRegularExpression), + }, + exclude: { + isValid: (value: string) => { + if (validators.isNotEmpty(value)) { + return validators.isRegExValid(value); + } + + return true; + }, + error: this.props.intl.formatMessage(MESSAGES.invalidRegularExpression), + }, + }; + + checkFieldValidity = throttle((fieldName: ValidatedFields, fieldValue) => { + const {errors} = this.state; + + if (errors == null) { + return; + } + + if (errors[fieldName] && this.validatedFields[fieldName].isValid(fieldValue)) { + delete errors[fieldName]; + this.setState({errors}); + } + }, 150); + + constructor(props: WrappedComponentProps) { + super(props); + + this.state = { + errors: {}, + isSubmitting: false, + isFormChanged: false, + currentlyEditingRule: null, + doesPatternMatchTest: false, + }; + } + + getAmendedFormData(): AddRuleOptions | null { + if (this.formRef == null) { + return null; + } + + const formData = this.formRef.getFormData() as Partial; + if (formData == null) { + return null; + } + + const feedIDs = [formData.feedID || '']; + + delete formData.feedID; + delete formData.check; + + return { + ...defaultRule, + ...formData, + feedIDs, + ...(formData.tags != null + ? { + tags: formData.tags.split(','), + } + : { + tags: [], + }), + }; + } + + getModifyRuleForm(rule: Partial) { + const {doesPatternMatchTest, currentlyEditingRule} = this.state; + const {feeds} = FeedStore; + + return ( + + + + + + + + + + + + {doesPatternMatchTest && ( + + + + )} + + + + + + + + + +
    + + + + + +
    +
    + ); + } + + getRulesListItem(rule: Rule) { + const matchedCount = rule.count || 0; + let excludeNode = null; + let tags = null; + + if (rule.exclude) { + excludeNode = ( +
  • + + {': '} + {rule.exclude} +
  • + ); + } + + if (rule.tags && rule.tags.length > 0) { + const tagNodes = rule.tags.map((tag) => ( + + {tag} + + )); + + tags = ( +
  • + + {': '} + {tagNodes} +
  • + ); + } + + return ( +
  • +
    +
      +
    • + {rule.label} +
    • +
    • + +
    • + {rule === this.state.currentlyEditingRule && ( +
    • + Modifying +
    • + )} +
    +
      +
    • + + {': '} + {rule.match} +
    • +
      + {excludeNode} + {tags} +
    +
    + this.handleModifyRuleClick(rule)}> + + + this.handleRemoveRuleClick(rule)}> + + +
  • + ); + } + + getRulesList(): [React.ReactNode, React.ReactNode] { + const {rules} = FeedStore; + + if (rules.length === 0) { + return [ +
      +
    • + +
    • +
    , + null, + ]; + } + + const rulesList = rules.map((rule) => this.getRulesListItem(rule)); + + if (this.state.currentlyEditingRule == null || this.state.currentlyEditingRule === defaultRule) { + return [ +
      + {rulesList} +
    , + null, + ]; + } + + const editingRuleIndex = rules.indexOf(this.state.currentlyEditingRule as Rule); + + return [ +
      + {rulesList.slice(0, editingRuleIndex + 1)} +
    , +
      + {rulesList.slice(editingRuleIndex + 1)} +
    , + ]; + } + + handleFormChange = ({ + event, + formData, + }: { + event: Event | React.FormEvent; + formData: Record; + }) => { + const validatedField = (event.target as HTMLInputElement).name as ValidatedFields; + const ruleFormData = formData as Partial; + this.checkFieldValidity(validatedField, ruleFormData[validatedField]); + this.checkMatchingPattern( + ruleFormData.match != null ? ruleFormData.match : defaultRule.match, + ruleFormData.exclude != null ? ruleFormData.exclude : defaultRule.exclude, + ruleFormData.check != null ? ruleFormData.check : '', + ); + this.setState({isFormChanged: true}); + }; + + handleFormSubmit = async () => { + const {errors, isValid} = this.validateForm(); + + this.setState({isSubmitting: true}); + + if (!isValid) { + this.setState({errors}); + } else { + const currentRule = this.state.currentlyEditingRule; + const formData = this.getAmendedFormData(); + + if (formData != null && this.state.isFormChanged) { + if (currentRule !== null && currentRule !== defaultRule && currentRule._id != null) { + await FeedActions.removeFeedMonitor(currentRule._id); + } + await FeedActions.addRule(formData); + } + + if (this.formRef != null) { + this.formRef.resetForm(); + } + + this.setState({currentlyEditingRule: null}); + } + + this.setState({isSubmitting: false}); + }; + + handleAddRuleClick = () => { + this.setState({currentlyEditingRule: defaultRule}); + }; + + handleRemoveRuleClick(rule: Rule) { + if (rule._id != null) { + FeedActions.removeFeedMonitor(rule._id); + } + + if (rule === this.state.currentlyEditingRule) { + this.setState({currentlyEditingRule: null}); + } + } + + handleModifyRuleClick(rule: Rule) { + this.setState({currentlyEditingRule: rule}); + } + + checkMatchingPattern(match: RuleFormData['match'], exclude: RuleFormData['exclude'], check: RuleFormData['check']) { + let doesPatternMatchTest = false; + + if (validators.isNotEmpty(check) && validators.isRegExValid(match) && validators.isRegExValid(exclude)) { + const isMatched = new RegExp(match, 'gi').test(check); + const isExcluded = exclude !== '' && new RegExp(exclude, 'gi').test(check); + doesPatternMatchTest = isMatched && !isExcluded; + } + + this.setState({doesPatternMatchTest}); + } + + validateForm(): { + errors?: DownloadRulesTabStates['errors']; + isValid: boolean; + } { + const formData = this.getAmendedFormData(); + + if (formData == null) { + return {isValid: false}; + } + + const errors = Object.keys(this.validatedFields).reduce((accumulator: DownloadRulesTabStates['errors'], field) => { + const fieldName = field as ValidatedFields; + const fieldValue = fieldName === 'feedID' ? formData.feedIDs[0] : formData[fieldName]; + + if (!this.validatedFields[fieldName].isValid(fieldValue) && accumulator != null) { + accumulator[fieldName] = this.validatedFields[fieldName].error; + } + + return accumulator; + }, {}); + + if (errors == null) { + return {isValid: true}; + } + + return {errors, isValid: !Object.keys(errors).length}; + } + + render() { + let errors = null; + if (this.state.errors != null) { + errors = Object.keys(this.state.errors).map((error) => { + const errorID = error as ValidatedFields; + if (this.state.errors?.[errorID] == null) { + return null; + } + return ( + + {this.state.errors[errorID]} + + ); + }); + } + + const [listBeforeEditingRule, listAfterEditingRule] = this.getRulesList(); + + return ( +
    { + this.formRef = ref; + }}> + + + + {errors} + {listAfterEditingRule == null ? ( + + {listBeforeEditingRule} + + ) : ( + + {listBeforeEditingRule} + {this.getModifyRuleForm(this.state.currentlyEditingRule as Partial)} + {listAfterEditingRule} + + )} + {this.state.currentlyEditingRule && listAfterEditingRule == null ? ( + this.getModifyRuleForm(this.state.currentlyEditingRule) + ) : ( + +
    + +
    + )} +
    + ); + } +} + +export default injectIntl(DownloadRulesTab); diff --git a/client/src/javascript/components/modals/feeds-modal/FeedsModal.js b/client/src/javascript/components/modals/feeds-modal/FeedsModal.js deleted file mode 100644 index b602acc77..000000000 --- a/client/src/javascript/components/modals/feeds-modal/FeedsModal.js +++ /dev/null @@ -1,47 +0,0 @@ -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import DownloadRulesTab from './DownloadRulesTab'; -import FeedMonitorStore from '../../../stores/FeedMonitorStore'; -import FeedsTab from './FeedsTab'; -import Modal from '../Modal'; - -class FeedsModal extends React.Component { - componentDidMount() { - FeedMonitorStore.fetchFeedMonitors(); - } - - render() { - const tabs = { - feeds: { - content: FeedsTab, - label: this.props.intl.formatMessage({ - id: 'feeds.tabs.feeds', - defaultMessage: 'Feeds', - }), - }, - downloadRules: { - content: DownloadRulesTab, - label: this.props.intl.formatMessage({ - id: 'feeds.tabs.download.rules', - defaultMessage: 'Download Rules', - }), - }, - }; - - return ( - - ); - } -} - -export default injectIntl(FeedsModal); diff --git a/client/src/javascript/components/modals/feeds-modal/FeedsModal.tsx b/client/src/javascript/components/modals/feeds-modal/FeedsModal.tsx new file mode 100644 index 000000000..1ff626036 --- /dev/null +++ b/client/src/javascript/components/modals/feeds-modal/FeedsModal.tsx @@ -0,0 +1,50 @@ +import {Component} from 'react'; +import {injectIntl, WrappedComponentProps} from 'react-intl'; + +import DownloadRulesTab from './DownloadRulesTab'; +import FeedActions from '../../../actions/FeedActions'; +import FeedStore from '../../../stores/FeedStore'; +import FeedsTab from './FeedsTab'; +import Modal from '../Modal'; + +class FeedsModal extends Component { + componentDidMount() { + FeedActions.fetchFeedMonitors(); + } + + render() { + const tabs = { + feeds: { + content: FeedsTab, + props: { + feedStore: FeedStore, + }, + label: this.props.intl.formatMessage({ + id: 'feeds.tabs.feeds', + }), + }, + downloadRules: { + content: DownloadRulesTab, + props: { + feedStore: FeedStore, + }, + label: this.props.intl.formatMessage({ + id: 'feeds.tabs.download.rules', + }), + }, + }; + + return ( + + ); + } +} + +export default injectIntl(FeedsModal); diff --git a/client/src/javascript/components/modals/feeds-modal/FeedsTab.js b/client/src/javascript/components/modals/feeds-modal/FeedsTab.js deleted file mode 100644 index c6ccefd34..000000000 --- a/client/src/javascript/components/modals/feeds-modal/FeedsTab.js +++ /dev/null @@ -1,528 +0,0 @@ -import _ from 'lodash'; -import {defineMessages, FormattedMessage, injectIntl} from 'react-intl'; -import { - Button, - Checkbox, - Form, - FormError, - FormRow, - FormRowGroup, - FormRowItem, - Select, - SelectItem, - Textbox, -} from 'flood-ui-kit'; -import formatUtil from '@shared/util/formatUtil'; -import React from 'react'; - -import Edit from '../../icons/Edit'; -import Close from '../../icons/Close'; -import EventTypes from '../../../constants/EventTypes'; -import FeedMonitorStore from '../../../stores/FeedMonitorStore'; -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import * as validators from '../../../util/validators'; -import UIActions from '../../../actions/UIActions'; -import connectStores from '../../../util/connectStores'; - -const MESSAGES = defineMessages({ - mustSpecifyURL: { - id: 'feeds.validation.must.specify.valid.feed.url', - defaultMessage: 'You must specify a valid feed URL.', - }, - mustSpecifyLabel: { - id: 'feeds.validation.must.specify.label', - defaultMessage: 'You must specify a label.', - }, - intervalNotPositive: { - id: 'feeds.validation.interval.not.positive', - defaultMessage: 'The interval must be a positive integer.', - }, - min: { - id: 'feeds.time.min', - defaultMessage: 'Minutes', - }, - hr: { - id: 'feeds.time.hr', - defaultMessage: 'Hours', - }, - day: { - id: 'feeds.time.day', - defaultMessage: 'Days', - }, - url: { - id: 'feeds.url', - defaultMessage: 'URL', - }, - label: { - id: 'feeds.label', - defaultMessage: 'Label', - }, - interval: { - id: 'feeds.interval', - defaultMessage: 'Interval', - }, - tags: { - id: 'feeds.tags', - defaultMessage: 'Tags', - }, - search: { - id: 'feeds.search', - defaultMessage: 'Search term', - }, -}); - -const defaultFeed = { - label: '', - interval: 5, - url: '', -}; - -class FeedsTab extends React.Component { - formRef = null; - - manualAddingFormRef = null; - - validatedFields = { - url: { - isValid: validators.isURLValid, - error: this.props.intl.formatMessage(MESSAGES.mustSpecifyURL), - }, - label: { - isValid: validators.isNotEmpty, - error: this.props.intl.formatMessage(MESSAGES.mustSpecifyLabel), - }, - interval: { - isValid: validators.isPositiveInteger, - error: this.props.intl.formatMessage(MESSAGES.intervalNotPositive), - }, - }; - - state = { - errors: {}, - intervalmultipliers: [ - { - displayName: this.props.intl.formatMessage(MESSAGES.min), - value: 1, - }, - { - displayName: this.props.intl.formatMessage(MESSAGES.hr), - value: 60, - }, - { - displayName: this.props.intl.formatMessage(MESSAGES.day), - value: 1440, - }, - ], - currentlyEditingFeed: null, - selectedFeed: null, - }; - - checkFieldValidity = _.throttle((fieldName, fieldValue) => { - const {errors} = this.state; - - if (this.state.errors[fieldName] && this.validatedFields[fieldName].isValid(fieldValue)) { - delete errors[fieldName]; - this.setState({errors}); - } - }, 150); - - getAmendedFormData() { - const formData = this.formRef.getFormData(); - formData.interval = (formData.interval * formData.intervalMultiplier).toString(); - delete formData.intervalMultiplier; - - return formData; - } - - getIntervalSelectOptions() { - return this.state.intervalmultipliers.map(interval => ( - - {interval.displayName} - - )); - } - - getAvailableFeedsOptions() { - const {feeds} = this.props; - - if (!feeds.length) { - return [ - - - - - , - ]; - } - - return feeds.reduce( - (feedOptions, feed) => - feedOptions.concat( - - {feed.label} - , - ), - [ - - - - - , - ], - ); - } - - getModifyFeedForm(feed) { - const isDayInterval = feed.interval % 1440; - const minutesDivisor = feed.interval % 60 ? 1 : 60; - const defaultIntervalTextValue = feed.interval / isDayInterval ? minutesDivisor : 1440; - const defaultIntervalMultiplierId = isDayInterval ? minutesDivisor : 1440; - - return ( - - - - - - - - - - - - - ); - } - - getFeedsListItem(feed) { - const matchedCount = feed.count || 0; - return ( -
  • -
    -
      -
    • - {feed.label} -
    • -
    • - -
    • - {feed === this.state.currentlyEditingFeed && ( -
    • - Modifying -
    • - )} -
    - -
    - this.handleModifyFeedClick(feed)}> - - - this.handleRemoveFeedClick(feed)}> - - -
  • - ); - } - - getFeedAddForm(errors) { - return ( -
    { - this.formRef = ref; - }}> - - - - {errors} - - {this.getFeedsList()} - - {this.state.currentlyEditingFeed ? ( - this.getModifyFeedForm(this.state.currentlyEditingFeed) - ) : ( - - - - - )} -
    - ); - } - - getFeedsList() { - const {feeds} = this.props; - - if (feeds.length === 0) { - return ( -
      -
    • - -
    • -
    - ); - } - - const feedsList = feeds.map(feed => this.getFeedsListItem(feed)); - - return
      {feedsList}
    ; - } - - getFeedItemsForm() { - return ( -
    { - this.manualAddingFormRef = ref; - }}> - - - - - - {this.renderSearchField()} - {this.renderDownloadButton()} - - {this.state.selectedFeed && {this.getFeedItemsList()}} -
    - ); - } - - getFeedItemsList() { - const {items} = this.props; - - if (items.length === 0) { - return ( -
      -
    • -
      - -
      -
    • -
    - ); - } - - const itemsList = items.map((item, index) => ( -
  • -
    {item.title}
    - -
  • - )); - - return
      {itemsList}
    ; - } - - handleFormSubmit = () => { - const {errors, isValid} = this.validateForm(); - - if (!isValid) { - this.setState({errors}); - } else { - const currentFeed = this.state.currentlyEditingFeed; - const formData = this.getAmendedFormData(); - - if (currentFeed === defaultFeed) { - FeedMonitorStore.addFeed(formData); - } else { - FeedMonitorStore.modifyFeed(currentFeed._id, formData); - } - this.formRef.resetForm(); - this.setState({currentlyEditingFeed: null}); - } - }; - - handleFormChange = ({event, formData}) => { - this.checkFieldValidity(event.target.name, formData[event.target.name]); - }; - - handleRemoveFeedClick = feed => { - FeedMonitorStore.removeFeed(feed._id); - - if (feed === this.state.currentlyEditingFeed) { - this.setState({currentlyEditingFeed: null}); - } - }; - - handleAddFeedClick = () => { - this.setState({currentlyEditingFeed: defaultFeed}); - }; - - handleModifyFeedClick = feed => { - this.setState({currentlyEditingFeed: feed}); - }; - - handleBrowseFeedChange = input => { - if (input.event.target.type !== 'checkbox') { - this.setState({selectedFeed: input.formData.feedID}); - FeedMonitorStore.fetchItems({params: {id: input.formData.feedID, search: input.formData.search}}); - } - }; - - handleBrowseFeedSubmit = () => { - const formData = this.manualAddingFormRef.getFormData(); - - const downloadedTorrents = this.props.items - .filter((item, index) => formData[index]) - .map((torrent, index) => ({id: index, value: torrent.link})); - - UIActions.displayModal({id: 'add-torrents', torrents: downloadedTorrents}); - }; - - validateForm() { - const formData = this.formRef.getFormData(); - const errors = Object.keys(this.validatedFields).reduce((memo, fieldName) => { - const fieldValue = formData[fieldName]; - - if (!this.validatedFields[fieldName].isValid(fieldValue)) { - memo[fieldName] = this.validatedFields[fieldName].error; - } - - return memo; - }, {}); - - return {errors, isValid: !Object.keys(errors).length}; - } - - renderSearchField = () => { - const {selectedFeed} = this.state; - - if (selectedFeed == null) return null; - - return ( - - ); - }; - - renderDownloadButton = () => { - const {selectedFeed} = this.state; - - if (selectedFeed == null) return null; - - return ( - - ); - }; - - render() { - const errors = Object.keys(this.state.errors).map(errorID => ( - - {this.state.errors[errorID]} - - )); - return ( -
    - {this.getFeedAddForm(errors)} - {this.getFeedItemsForm()} -
    - ); - } -} - -const ConnectedFeedsTab = connectStores(injectIntl(FeedsTab), () => { - return [ - { - store: FeedMonitorStore, - event: EventTypes.SETTINGS_FEED_MONITORS_FETCH_SUCCESS, - getValue: ({store}) => { - return { - feeds: store.getFeeds(), - }; - }, - }, - { - store: FeedMonitorStore, - event: EventTypes.SETTINGS_FEED_MONITOR_ITEMS_FETCH_SUCCESS, - getValue: ({store}) => { - return { - items: store.getItems() || [], - }; - }, - }, - ]; -}); - -export default ConnectedFeedsTab; diff --git a/client/src/javascript/components/modals/feeds-modal/FeedsTab.tsx b/client/src/javascript/components/modals/feeds-modal/FeedsTab.tsx new file mode 100644 index 000000000..2160bd288 --- /dev/null +++ b/client/src/javascript/components/modals/feeds-modal/FeedsTab.tsx @@ -0,0 +1,605 @@ +import {defineMessages, FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; +import {observer} from 'mobx-react'; +import * as React from 'react'; +import throttle from 'lodash/throttle'; + +import type {Feed} from '@shared/types/Feed'; + +import { + Button, + Checkbox, + Form, + FormError, + FormRow, + FormRowGroup, + FormRowItem, + Select, + SelectItem, + Textbox, +} from '../../../ui'; +import Close from '../../icons/Close'; +import Edit from '../../icons/Edit'; +import FeedActions from '../../../actions/FeedActions'; +import FeedStore from '../../../stores/FeedStore'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; +import UIActions from '../../../actions/UIActions'; +import * as validators from '../../../util/validators'; + +type ValidatedFields = 'url' | 'label' | 'interval'; + +interface FeedFormData extends Feed { + url: string; + label: string; + interval: number; + intervalMultiplier: number; +} + +interface FeedsTabStates { + errors?: { + [field in ValidatedFields]?: string; + }; + currentlyEditingFeed: Partial | null; + selectedFeedID: string | null; +} + +const MESSAGES = defineMessages({ + mustSpecifyURL: { + id: 'feeds.validation.must.specify.valid.feed.url', + }, + mustSpecifyLabel: { + id: 'feeds.validation.must.specify.label', + }, + intervalNotPositive: { + id: 'feeds.validation.interval.not.positive', + }, + min: { + id: 'feeds.time.min', + }, + hr: { + id: 'feeds.time.hr', + }, + day: { + id: 'feeds.time.day', + }, + url: { + id: 'feeds.url', + }, + label: { + id: 'feeds.label', + }, + interval: { + id: 'feeds.interval', + }, + tags: { + id: 'feeds.tags', + }, + search: { + id: 'feeds.search', + }, +}); + +const INTERVAL_MULTIPLIERS = [ + { + message: MESSAGES.min, + value: 1, + }, + { + message: MESSAGES.hr, + value: 60, + }, + { + message: MESSAGES.day, + value: 1440, + }, +] as const; + +const defaultFeed = { + label: '', + interval: 5, + url: '', +}; + +@observer +class FeedsTab extends React.Component { + formRef: Form | null = null; + + manualAddingFormRef: Form | null = null; + + validatedFields = { + url: { + isValid: validators.isURLValid, + error: this.props.intl.formatMessage(MESSAGES.mustSpecifyURL), + }, + label: { + isValid: validators.isNotEmpty, + error: this.props.intl.formatMessage(MESSAGES.mustSpecifyLabel), + }, + interval: { + isValid: validators.isPositiveInteger, + error: this.props.intl.formatMessage(MESSAGES.intervalNotPositive), + }, + }; + + checkFieldValidity = throttle((fieldName: ValidatedFields, fieldValue) => { + const {errors} = this.state; + + if (errors == null) { + return; + } + + if (errors[fieldName] && this.validatedFields[fieldName].isValid(fieldValue)) { + delete errors[fieldName]; + this.setState({errors}); + } + }, 150); + + constructor(props: WrappedComponentProps) { + super(props); + + this.state = { + errors: {}, + currentlyEditingFeed: null, + selectedFeedID: null, + }; + } + + getAmendedFormData(): Pick | null { + if (this.formRef == null) { + return null; + } + + const formData = this.formRef.getFormData() as Partial; + + const {url, label} = formData; + if (url == null || label == null) { + return null; + } + + let {interval} = defaultFeed; + if (formData.interval != null && formData.intervalMultiplier != null) { + interval = formData.interval * formData.intervalMultiplier; + } + + return {url, label, interval}; + } + + getIntervalSelectOptions() { + return INTERVAL_MULTIPLIERS.map((interval) => ( + + {this.props.intl.formatMessage(interval.message)} + + )); + } + + getModifyFeedForm(feed: Partial) { + const feedInterval = feed.interval || defaultFeed.interval; + + let defaultIntervalTextValue = feedInterval; + let defaultIntervalMultiplier = 1; + + INTERVAL_MULTIPLIERS.forEach((interval) => { + const intervalMultiplier = interval.value; + + if (feedInterval % intervalMultiplier === 0) { + defaultIntervalTextValue = feedInterval / intervalMultiplier; + defaultIntervalMultiplier = intervalMultiplier; + } + }); + + return ( + + + + + + + + + + + + + ); + } + + getFeedsListItem(feed: Feed) { + const {intl} = this.props; + const matchedCount = feed.count || 0; + + let intervalText = `${feed.interval}`; + let intervalMultiplierMessage = INTERVAL_MULTIPLIERS[0].message; + + INTERVAL_MULTIPLIERS.forEach((interval) => { + if (feed.interval % interval.value === 0) { + intervalText = `${feed.interval / interval.value}`; + intervalMultiplierMessage = interval.message; + } + }); + + return ( +
  • +
    +
      +
    • + {feed.label} +
    • +
    • + +
    • + {feed === this.state.currentlyEditingFeed && ( +
    • + Modifying +
    • + )} +
    +
      +
    • + {`${intervalText} ${intl.formatMessage(intervalMultiplierMessage)}`} +
    • +
    • + + {feed.url} + +
    • +
    +
    + this.handleModifyFeedClick(feed)}> + + + this.handleRemoveFeedClick(feed)}> + + +
  • + ); + } + + getFeedAddForm(errors: React.ReactNode) { + return ( +
    { + this.formRef = ref; + }}> + + + + {errors} + + {this.getFeedsList()} + + {this.state.currentlyEditingFeed ? ( + this.getModifyFeedForm(this.state.currentlyEditingFeed) + ) : ( + + {null} + + + )} +
    + ); + } + + getFeedsList() { + const {feeds} = FeedStore; + + if (feeds.length === 0) { + return ( +
      +
    • + +
    • +
    + ); + } + + const feedsList = feeds.map((feed) => this.getFeedsListItem(feed)); + + return
      {feedsList}
    ; + } + + getFeedItemsForm() { + const {feeds, items} = FeedStore; + + const itemElements: React.ReactNodeArray = []; + if (this.state.selectedFeedID) { + const titleOccurrences: Record = {}; + items.forEach((item, index) => { + let {title} = item; + const occurrence = titleOccurrences[title]; + + if (occurrence == null) { + titleOccurrences[title] = 2; + } else { + title = `${title} #${occurrence}`; + titleOccurrences[title] += 1; + } + + itemElements.push( +
  • +
    {title}
    + +
  • , + ); + }); + } + + return ( +
    { + this.manualAddingFormRef = ref; + }}> + + + + + + {this.renderSearchField()} + {this.renderDownloadButton()} + + {this.state.selectedFeedID ? ( + + {itemElements.length === 0 ? ( +
      +
    • +
      + +
      +
    • +
    + ) : ( +
      {itemElements}
    + )} +
    + ) : null} +
    + ); + } + + handleFormSubmit = () => { + const {errors, isValid} = this.validateForm(); + + if (!isValid) { + this.setState({errors}); + } else { + const currentFeed = this.state.currentlyEditingFeed; + const formData = this.getAmendedFormData(); + + if (formData != null) { + if (currentFeed === defaultFeed) { + FeedActions.addFeed(formData); + } else if (currentFeed?._id != null) { + FeedActions.modifyFeed(currentFeed._id, formData); + } + } + if (this.formRef != null) { + this.formRef.resetForm(); + } + this.setState({currentlyEditingFeed: null}); + } + }; + + handleFormChange = ({ + event, + formData, + }: { + event: Event | React.FormEvent; + formData: Record; + }) => { + const validatedField = (event.target as HTMLInputElement).name as ValidatedFields; + const feedForm = formData as Partial; + this.checkFieldValidity(validatedField, feedForm[validatedField]); + }; + + handleRemoveFeedClick = (feed: Feed) => { + if (feed._id != null) { + FeedActions.removeFeedMonitor(feed._id); + } + + if (feed === this.state.currentlyEditingFeed) { + this.setState({currentlyEditingFeed: null}); + } + }; + + handleAddFeedClick = () => { + this.setState({currentlyEditingFeed: defaultFeed}); + }; + + handleModifyFeedClick = (feed: Feed) => { + this.setState({currentlyEditingFeed: feed}); + }; + + handleBrowseFeedChange = (input: { + event: Event | React.FormEvent; + formData: Record; + }) => { + const feedBrowseForm = input.formData as {feedID: string; search: string}; + if ((input.event.target as HTMLInputElement).type !== 'checkbox') { + this.setState({selectedFeedID: feedBrowseForm.feedID}); + FeedActions.fetchItems({ + id: feedBrowseForm.feedID, + search: feedBrowseForm.search, + }); + } + }; + + handleBrowseFeedSubmit = () => { + if (this.manualAddingFormRef == null) { + return; + } + + const formData = this.manualAddingFormRef.getFormData(); + + // TODO: Properly handle array of array of URLs + const torrentsToDownload = FeedStore.items + .filter((_item, index) => formData[index]) + .map((item, index) => ({id: index, value: item.urls[0]})); + + UIActions.displayModal({ + id: 'add-torrents', + initialURLs: torrentsToDownload, + }); + }; + + validateForm(): {errors?: FeedsTabStates['errors']; isValid: boolean} { + if (this.formRef == null) { + return {isValid: false}; + } + + const formData = this.formRef.getFormData(); + const errors = Object.keys(this.validatedFields).reduce((memo: FeedsTabStates['errors'], field) => { + const fieldName = field as ValidatedFields; + const fieldValue = `${formData[fieldName]}`; + + return { + ...memo, + ...(!this.validatedFields[fieldName].isValid(fieldValue) && memo != null + ? {fieldName: this.validatedFields[fieldName].error} + : {}), + }; + }, {}); + + if (errors == null) { + return {isValid: true}; + } + + return {errors, isValid: !Object.keys(errors).length}; + } + + renderSearchField = () => { + const {selectedFeedID} = this.state; + + if (selectedFeedID == null) return null; + + return ( + + ); + }; + + renderDownloadButton = () => { + const {selectedFeedID} = this.state; + + if (selectedFeedID == null) return null; + + return ( + + ); + }; + + render() { + let errors = null; + if (this.state.errors != null) { + errors = Object.keys(this.state.errors).map((error) => { + const errorID = error as ValidatedFields; + if (this.state.errors?.[errorID] == null) { + return null; + } + return ( + + {this.state.errors[errorID]} + + ); + }); + } + return ( +
    + {this.getFeedAddForm(errors)} + {this.getFeedItemsForm()} +
    + ); + } +} + +export default injectIntl(FeedsTab); diff --git a/client/src/javascript/components/modals/generate-magnet-modal/GenerateMagnetModal.tsx b/client/src/javascript/components/modals/generate-magnet-modal/GenerateMagnetModal.tsx new file mode 100644 index 000000000..3a8f5f843 --- /dev/null +++ b/client/src/javascript/components/modals/generate-magnet-modal/GenerateMagnetModal.tsx @@ -0,0 +1,145 @@ +import {FC, useEffect, useRef, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import {TorrentTrackerType} from '@shared/types/TorrentTracker'; + +import Checkmark from '../../icons/Checkmark'; +import ClipboardIcon from '../../icons/ClipboardIcon'; +import {Form, FormElementAddon, FormError, FormRow, Textbox} from '../../../ui'; +import Modal from '../Modal'; +import TorrentActions from '../../../actions/TorrentActions'; +import TorrentStore from '../../../stores/TorrentStore'; + +const generateMagnet = (hash: string, trackers?: Array): string => { + let result = `magnet:?xt=urn:btih:${hash}`; + + if (trackers?.length) { + trackers.forEach((tracker) => { + result = `${result}&tr=${encodeURI(tracker)}`; + }); + } + + return result; +}; + +const GenerateMagnetModal: FC = () => { + const magnetTextboxRef = useRef(null); + const magnetTrackersTextboxRef = useRef(null); + const intl = useIntl(); + + const [isMagnetCopied, setIsMagnetCopied] = useState(false); + const [isMagnetTrackersCopied, setIsMagnetTrackersCopied] = useState(false); + const [trackerState, setTrackerState] = useState<{ + isLoadingTrackers: boolean; + magnetTrackersLink: string; + }>({ + isLoadingTrackers: true, + magnetTrackersLink: '', + }); + + useEffect(() => { + TorrentActions.fetchTorrentTrackers(TorrentStore.selectedTorrents[0]).then((trackers) => { + if (trackers != null) { + setTrackerState({ + isLoadingTrackers: false, + magnetTrackersLink: generateMagnet( + TorrentStore.selectedTorrents[0], + trackers.filter((tracker) => tracker.type !== TorrentTrackerType.DHT).map((tracker) => tracker.url), + ), + }); + } + }); + }, []); + + const magnetLink = generateMagnet(TorrentStore.selectedTorrents[0]); + + return ( + +
    + {TorrentStore.torrents[TorrentStore.selectedTorrents[0]]?.isPrivate ? ( + + {intl.formatMessage({id: 'torrents.generate.magnet.private.torrent'})} + + ) : null} + + + { + if (typeof navigator.clipboard?.writeText === 'function') { + navigator.clipboard.writeText(magnetLink).then(() => { + setIsMagnetCopied(true); + }); + } else if (magnetTextboxRef.current != null) { + magnetTextboxRef.current?.select(); + document.execCommand('copy'); + setIsMagnetCopied(true); + } + }}> + {isMagnetCopied ? : } + + + + + {trackerState.isLoadingTrackers ? ( + + ) : ( + + { + if (typeof navigator.clipboard?.writeText === 'function') { + navigator.clipboard.writeText(trackerState.magnetTrackersLink).then(() => { + setIsMagnetTrackersCopied(true); + }); + } else if (magnetTrackersTextboxRef.current != null) { + magnetTrackersTextboxRef.current?.select(); + document.execCommand('copy'); + setIsMagnetTrackersCopied(true); + } + }}> + {isMagnetTrackersCopied ? : } + + + )} + +
    +
    + } + actions={[ + { + clickHandler: null, + content: intl.formatMessage({ + id: 'button.close', + }), + triggerDismiss: true, + type: 'tertiary', + }, + ]} + /> + ); +}; + +export default GenerateMagnetModal; diff --git a/client/src/javascript/components/modals/move-torrents-modal/MoveTorrentsModal.js b/client/src/javascript/components/modals/move-torrents-modal/MoveTorrentsModal.js deleted file mode 100644 index 613670d23..000000000 --- a/client/src/javascript/components/modals/move-torrents-modal/MoveTorrentsModal.js +++ /dev/null @@ -1,110 +0,0 @@ -import {Form} from 'flood-ui-kit'; -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import Modal from '../Modal'; -import ModalActions from '../ModalActions'; -import TorrentActions from '../../../actions/TorrentActions'; -import TorrentDestination from '../../general/filesystem/TorrentDestination'; -import TorrentStore from '../../../stores/TorrentStore'; - -class MoveTorrents extends React.Component { - constructor(props) { - super(props); - const filenames = TorrentStore.getSelectedTorrentsFilename(); - const sources = TorrentStore.getSelectedTorrentsDownloadLocations(); - - this.state = { - isSettingDownloadPath: false, - originalSource: sources.length === 1 ? this.removeTrailingFilename(sources[0], filenames[0]) : null, - }; - } - - getActions() { - return [ - { - checked: false, - content: this.props.intl.formatMessage({ - id: 'torrents.move.data.label', - defaultMessage: 'Move data', - }), - id: 'moveFiles', - type: 'checkbox', - }, - { - content: this.props.intl.formatMessage({ - id: 'button.cancel', - defaultMessage: 'Cancel', - }), - triggerDismiss: true, - type: 'tertiary', - }, - { - content: this.props.intl.formatMessage({ - id: 'torrents.move.button.set.location', - defaultMessage: 'Set Location', - }), - isLoading: this.state.isSettingDownloadPath, - submit: true, - type: 'primary', - }, - ]; - } - - getContent() { - return ( -
    -
    - - - -
    - ); - } - - handleFormSubmit = ({formData}) => { - const filenames = TorrentStore.getSelectedTorrentsFilename(); - const sourcePaths = TorrentStore.getSelectedTorrentsDownloadLocations(); - - if (sourcePaths.length) { - this.setState({isSettingDownloadPath: true}); - TorrentActions.moveTorrents(TorrentStore.getSelectedTorrents(), { - destination: formData.destination, - isBasePath: formData.useBasePath, - filenames, - moveFiles: formData.moveFiles, - sourcePaths, - }).then(() => { - this.setState({isSettingDownloadPath: false}); - }); - } - }; - - removeTrailingFilename(path, filename) { - let directoryPath = path.substring(0, path.length - filename.length); - - if ( - directoryPath.charAt(directoryPath.length - 1) === '/' || - directoryPath.charAt(directoryPath.length - 1) === '\\' - ) { - directoryPath = directoryPath.substring(0, directoryPath.length - 1); - } - - return directoryPath; - } - - render() { - return ( - - ); - } -} - -export default injectIntl(MoveTorrents); diff --git a/client/src/javascript/components/modals/move-torrents-modal/MoveTorrentsModal.tsx b/client/src/javascript/components/modals/move-torrents-modal/MoveTorrentsModal.tsx new file mode 100644 index 000000000..2efc459d1 --- /dev/null +++ b/client/src/javascript/components/modals/move-torrents-modal/MoveTorrentsModal.tsx @@ -0,0 +1,111 @@ +import {FC, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import FilesystemBrowserTextbox from '../../general/form-elements/FilesystemBrowserTextbox'; +import {Form} from '../../../ui'; +import Modal from '../Modal'; +import ModalActions from '../ModalActions'; +import TorrentActions from '../../../actions/TorrentActions'; +import TorrentStore from '../../../stores/TorrentStore'; +import UIStore from '../../../stores/UIStore'; + +const getSuggestedPath = (sources: Array): string | undefined => { + const commonPath = sources[0]; + + if ( + !sources.some((path) => { + if (!path.includes(commonPath)) { + // Bail out if at least one selected torrent doesn't match. + return true; + } + return false; + }) + ) { + // If every selected torrent shares a common path, suggest it. + return commonPath; + } + + // Fallback to default download location. + return undefined; +}; + +const MoveTorrents: FC = () => { + const intl = useIntl(); + const [isSettingDownloadPath, setIsSettingDownloadPath] = useState(false); + + return ( + +
    { + const hashes = TorrentStore.selectedTorrents; + if (hashes.length > 0) { + setIsSettingDownloadPath(true); + TorrentActions.moveTorrents({ + hashes, + destination: formData.destination as string, + isBasePath: formData.isBasePath as boolean, + moveFiles: formData.moveFiles as boolean, + isCheckHash: formData.isCheckHash as boolean, + }).then(() => { + UIStore.dismissModal(); + setIsSettingDownloadPath(false); + }); + } + }}> + TorrentStore.torrents[hash].directory), + )} + showBasePathToggle + /> + + +
    + } + /> + ); +}; + +export default MoveTorrents; diff --git a/client/src/javascript/components/modals/remove-torrents-modal/RemoveTorrentsModal.js b/client/src/javascript/components/modals/remove-torrents-modal/RemoveTorrentsModal.js deleted file mode 100644 index a32f40039..000000000 --- a/client/src/javascript/components/modals/remove-torrents-modal/RemoveTorrentsModal.js +++ /dev/null @@ -1,117 +0,0 @@ -import {Checkbox, Form, FormRow} from 'flood-ui-kit'; -import {FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import Modal from '../Modal'; -import SettingsStore from '../../../stores/SettingsStore'; -import TorrentActions from '../../../actions/TorrentActions'; -import TorrentStore from '../../../stores/TorrentStore'; - -class RemoveTorrentsModal extends React.Component { - getActions(torrents) { - if (torrents.length === 0) { - return [ - { - clickHandler: null, - content: 'OK', - triggerDismiss: true, - type: 'primary', - }, - ]; - } - - return [ - { - clickHandler: this.handleRemoveTorrentDecline, - content: this.props.intl.formatMessage({ - id: 'button.no', - defaultMessage: 'No', - }), - triggerDismiss: true, - type: 'tertiary', - }, - { - clickHandler: this.handleRemovalConfirmation, - content: this.props.intl.formatMessage({ - id: 'button.yes', - defaultMessage: 'Yes', - }), - triggerDismiss: true, - type: 'primary', - }, - ]; - } - - getContent(torrents) { - let modalContent = null; - let deleteDataContent = null; - const selectedTorrentCount = torrents.length; - - if (selectedTorrentCount === 0) { - modalContent = ( - - ); - } else { - modalContent = ( - - ); - - deleteDataContent = ( - - - - - - ); - } - - return ( -
    -
    { - this.formRef = ref; - }}> - {modalContent} - {deleteDataContent} -
    -
    - ); - } - - handleRemovalConfirmation = () => { - const torrents = TorrentStore.getSelectedTorrents(); - const formData = this.formRef.getFormData(); - TorrentActions.deleteTorrents(torrents, formData.deleteData); - }; - - render() { - const selectedTorrents = TorrentStore.getSelectedTorrents(); - const modalHeading = this.props.intl.formatMessage({ - id: 'torrents.remove', - defaultMessage: 'Remove Torrents', - }); - - return ( - - ); - } -} - -export default injectIntl(RemoveTorrentsModal); diff --git a/client/src/javascript/components/modals/remove-torrents-modal/RemoveTorrentsModal.tsx b/client/src/javascript/components/modals/remove-torrents-modal/RemoveTorrentsModal.tsx new file mode 100644 index 000000000..a3eb37c3c --- /dev/null +++ b/client/src/javascript/components/modals/remove-torrents-modal/RemoveTorrentsModal.tsx @@ -0,0 +1,107 @@ +import {FC, useState} from 'react'; +import {FormattedMessage, useIntl} from 'react-intl'; + +import {Form, FormRow} from '../../../ui'; +import Modal from '../Modal'; +import ModalActions from '../ModalActions'; +import {saveDeleteTorrentsUserPreferences} from '../../../util/userPreferences'; +import SettingStore from '../../../stores/SettingStore'; +import TorrentActions from '../../../actions/TorrentActions'; +import TorrentStore from '../../../stores/TorrentStore'; +import UIStore from '../../../stores/UIStore'; + +const RemoveTorrentsModal: FC = () => { + const intl = useIntl(); + const [isRemoving, setIsRemoving] = useState(false); + const {selectedTorrents} = TorrentStore; + + if (selectedTorrents.length === 0) { + return ( + +
    + + + +
    +
    + } + actions={[ + { + clickHandler: null, + content: intl.formatMessage({ + id: 'button.ok', + }), + triggerDismiss: true, + type: 'primary', + }, + ]} + /> + ); + } + + return ( + +
    { + setIsRemoving(true); + + const deleteData = formData.deleteData as boolean; + + TorrentActions.deleteTorrents({ + hashes: TorrentStore.selectedTorrents, + deleteData, + }).then(() => { + setIsRemoving(false); + saveDeleteTorrentsUserPreferences({deleteData}); + UIStore.dismissModal(); + }); + }}> + + + + + +
    + } + /> + ); +}; + +export default RemoveTorrentsModal; diff --git a/client/src/javascript/components/modals/set-tags-modal/SetTagsModal.js b/client/src/javascript/components/modals/set-tags-modal/SetTagsModal.js deleted file mode 100644 index c4a3bdb71..000000000 --- a/client/src/javascript/components/modals/set-tags-modal/SetTagsModal.js +++ /dev/null @@ -1,88 +0,0 @@ -import {Form, FormRow, Textbox} from 'flood-ui-kit'; -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import Modal from '../Modal'; -import TorrentActions from '../../../actions/TorrentActions'; -import TorrentStore from '../../../stores/TorrentStore'; - -class SetTagsModal extends React.Component { - state = { - isSettingTags: false, - }; - - formRef = null; - - handleSetTagsClick = () => { - const formData = this.formRef.getFormData(); - const tags = formData.tags ? formData.tags.split(',') : []; - - this.setState({isSettingTags: true}, () => TorrentActions.setTaxonomy(TorrentStore.getSelectedTorrents(), tags)); - }; - - getActions() { - const primaryButtonText = this.props.intl.formatMessage({ - id: 'torrents.set.tags.button.set', - defaultMessage: 'Set Tags', - }); - - return [ - { - clickHandler: null, - content: this.props.intl.formatMessage({ - id: 'button.cancel', - defaultMessage: 'Cancel', - }), - triggerDismiss: true, - type: 'tertiary', - }, - { - clickHandler: this.handleSetTagsClick, - content: primaryButtonText, - isLoading: this.state.isSettingTags, - triggerDismiss: false, - type: 'primary', - }, - ]; - } - - getContent() { - const tagsValue = TorrentStore.getSelectedTorrentsTags()[0].join(', '); - - return ( -
    -
    { - this.formRef = ref; - }}> - - - -
    -
    - ); - } - - render() { - return ( - - ); - } -} - -export default injectIntl(SetTagsModal); diff --git a/client/src/javascript/components/modals/set-tags-modal/SetTagsModal.tsx b/client/src/javascript/components/modals/set-tags-modal/SetTagsModal.tsx new file mode 100644 index 000000000..28a4f99d0 --- /dev/null +++ b/client/src/javascript/components/modals/set-tags-modal/SetTagsModal.tsx @@ -0,0 +1,77 @@ +import {FC, useRef, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import {Form, FormRow} from '../../../ui'; +import Modal from '../Modal'; +import TagSelect from '../../general/form-elements/TagSelect'; +import TorrentActions from '../../../actions/TorrentActions'; +import TorrentStore from '../../../stores/TorrentStore'; + +const SetTagsModal: FC = () => { + const formRef = useRef
    (null); + const intl = useIntl(); + const [isSettingTags, setIsSettingTags] = useState(false); + + return ( + + + + TorrentStore.torrents[hash].tags)[0] + .slice()} + id="tags" + placeholder={intl.formatMessage({ + id: 'torrents.set.tags.enter.tags', + })} + /> + + +
    + } + actions={[ + { + content: intl.formatMessage({ + id: 'button.cancel', + }), + clickHandler: null, + triggerDismiss: true, + type: 'tertiary', + }, + { + content: intl.formatMessage({ + id: 'torrents.set.tags.button.set', + }), + clickHandler: () => { + if (formRef.current == null) { + return; + } + + const {selectedTorrents} = TorrentStore; + const formData = formRef.current.getFormData() as {tags: string}; + const tags = formData.tags ? formData.tags.split(',') : []; + + setIsSettingTags(true); + + if (selectedTorrents?.length > 0) { + TorrentActions.setTags({ + hashes: selectedTorrents as [string, ...string[]], + tags, + }); + } + }, + isLoading: isSettingTags, + triggerDismiss: false, + type: 'primary', + }, + ]} + /> + ); +}; + +export default SetTagsModal; diff --git a/client/src/javascript/components/modals/set-trackers-modal/SetTrackersModal.tsx b/client/src/javascript/components/modals/set-trackers-modal/SetTrackersModal.tsx new file mode 100644 index 000000000..77ef4a2d5 --- /dev/null +++ b/client/src/javascript/components/modals/set-trackers-modal/SetTrackersModal.tsx @@ -0,0 +1,115 @@ +import {FC, useEffect, useRef, useState} from 'react'; +import {useIntl} from 'react-intl'; + +import {TorrentTrackerType} from '@shared/types/TorrentTracker'; + +import {Form, FormRow, Textbox} from '../../../ui'; +import Modal from '../Modal'; +import TextboxRepeater, {getTextArray} from '../../general/form-elements/TextboxRepeater'; +import TorrentActions from '../../../actions/TorrentActions'; +import TorrentStore from '../../../stores/TorrentStore'; +import UIStore from '../../../stores/UIStore'; + +const SetTrackersModal: FC = () => { + const formRef = useRef
    (null); + const intl = useIntl(); + const [isSettingTrackers, setIsSettingTrackers] = useState(false); + const [trackerState, setTrackerState] = useState<{ + isLoadingTrackers: boolean; + trackerURLs: Array; + }>({ + isLoadingTrackers: true, + trackerURLs: [], + }); + + useEffect(() => { + TorrentActions.fetchTorrentTrackers(TorrentStore.selectedTorrents[0]).then((trackers) => { + if (trackers != null) { + setTrackerState({ + isLoadingTrackers: false, + trackerURLs: trackers + .filter((tracker) => tracker.type !== TorrentTrackerType.DHT) + .map((tracker) => tracker.url), + }); + } + }); + }, []); + + return ( + + + {trackerState.isLoadingTrackers ? ( + + + + ) : ( + ({ + id: index, + value: url, + })) + } + /> + )} + +
    + } + actions={[ + { + clickHandler: null, + content: intl.formatMessage({ + id: 'button.cancel', + }), + triggerDismiss: true, + type: 'tertiary', + }, + { + clickHandler: () => { + if (formRef.current == null || isSettingTrackers || trackerState.isLoadingTrackers) { + return; + } + + setIsSettingTrackers(true); + + const formData = formRef.current.getFormData() as Record; + const trackers = getTextArray(formData, 'trackers').filter((tracker) => tracker !== ''); + + TorrentActions.setTrackers({ + hashes: TorrentStore.selectedTorrents, + trackers, + }).then(() => { + setIsSettingTrackers(false); + UIStore.dismissModal(); + }); + }, + content: intl.formatMessage({ + id: 'torrents.set.trackers.button.set', + }), + isLoading: isSettingTrackers || trackerState.isLoadingTrackers, + triggerDismiss: false, + type: 'primary', + }, + ]} + /> + ); +}; + +export default SetTrackersModal; diff --git a/client/src/javascript/components/modals/settings-modal/AboutTab.js b/client/src/javascript/components/modals/settings-modal/AboutTab.js deleted file mode 100644 index 3c1106082..000000000 --- a/client/src/javascript/components/modals/settings-modal/AboutTab.js +++ /dev/null @@ -1,22 +0,0 @@ -import axios from 'axios'; -import React from 'react'; -import ReactMarkdown from 'react-markdown'; - -import AboutMarkdownPath from '../../../../../../ABOUT.md'; -import SettingsTab from './SettingsTab'; - -export default class AboutTab extends SettingsTab { - state = { - about: null, - }; - - componentDidMount() { - axios.get(AboutMarkdownPath).then(response => { - this.setState({about: response.data}); - }); - } - - render() { - return ; - } -} diff --git a/client/src/javascript/components/modals/settings-modal/AboutTab.tsx b/client/src/javascript/components/modals/settings-modal/AboutTab.tsx new file mode 100644 index 000000000..243b3c212 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/AboutTab.tsx @@ -0,0 +1,35 @@ +import {lazy, Suspense} from 'react'; + +import packageJSON from '../../../../../../package.json'; + +const AboutMarkdown = lazy(() => + import(/* webpackChunkName: 'about' */ '../../../../../../ABOUT.md').then((module) => ({default: module.react})), +); + +const FLOOD_PROJECT_URL = 'https://github.com/jesec/flood'; + +const AboutTab = () => ( + + packageJSON.version} + CommitBadge={() => + packageJSON.version.length > 8 ? ( + // If user is on a rolling build, display latest version of rolling build. + + Latest version of rolling build + + ) : ( + // If user is on a released build, display commits to project made since user's version. + + Commits since user's version + + ) + } + /> + +); + +export default AboutTab; diff --git a/client/src/javascript/components/modals/settings-modal/AuthTab.js b/client/src/javascript/components/modals/settings-modal/AuthTab.js deleted file mode 100644 index c0d68be9a..000000000 --- a/client/src/javascript/components/modals/settings-modal/AuthTab.js +++ /dev/null @@ -1,224 +0,0 @@ -import {Button, Checkbox, Form, FormError, FormRowItem, FormRow, LoadingRing, Textbox} from 'flood-ui-kit'; -import classnames from 'classnames'; -import {CSSTransition, TransitionGroup} from 'react-transition-group'; -import {FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import AuthActions from '../../../actions/AuthActions'; -import AuthStore from '../../../stores/AuthStore'; -import Close from '../../icons/Close'; -import connectStores from '../../../util/connectStores'; -import EventTypes from '../../../constants/EventTypes'; -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import RtorrentConnectionTypeSelection from '../../general/RtorrentConnectionTypeSelection'; -import SettingsTab from './SettingsTab'; - -class AuthTab extends SettingsTab { - state = { - addUserError: null, - hasFetchedUserList: false, - isAddingUser: false, - }; - - formData = {}; - - formRef = null; - - componentDidMount() { - if (!this.props.isAdmin) return; - - AuthActions.fetchUsers().then(() => { - this.setState({hasFetchedUserList: true}); - }); - } - - getUserList() { - const userList = this.props.users.sort((a, b) => a.username.localeCompare(b.username)); - - const currentUsername = AuthStore.getCurrentUsername(); - - return userList.map(user => { - const isCurrentUser = user.username === currentUsername; - let badge = null; - let removeIcon = null; - - if (!isCurrentUser) { - removeIcon = ( - - - - ); - } else { - badge = ( - - - - ); - } - - const classes = classnames('interactive-list__item', { - 'interactive-list__item--disabled': isCurrentUser, - }); - - return ( -
  • - -
    {user.username}
    - {badge} -
    - {removeIcon} -
  • - ); - }); - } - - handleDeleteUserClick(username) { - AuthActions.deleteUser(username).then(AuthActions.fetchUsers); - } - - handleFormChange = ({formData}) => { - this.formData = formData; - }; - - handleFormSubmit = () => { - if (this.formData.username === '') { - this.setState({ - addUserError: this.props.intl.formatMessage({ - id: 'auth.error.username.empty', - defaultMessage: 'Username cannot be empty.', - }), - }); - } else { - this.setState({isAddingUser: true}); - - AuthActions.createUser({ - username: this.formData.username, - password: this.formData.password, - host: this.formData.rtorrentHost, - port: this.formData.rtorrentPort, - socketPath: this.formData.rtorrentSocketPath, - isAdmin: this.formData.isAdmin === '1', - }) - .then(AuthActions.fetchUsers, error => { - this.setState({addUserError: error.response.data.message, isAddingUser: false}); - }) - .then(() => { - this.formRef.resetForm(); - this.setState({addUserError: null, isAddingUser: false}); - }); - } - }; - - render() { - if (!this.props.isAdmin) { - return ( -
    - - - - - - - - -
    - ); - } - - const isLoading = !this.state.hasFetchedUserList && this.props.users.length === 0; - const interactiveListClasses = classnames('interactive-list', { - 'interactive-list--loading': isLoading, - }); - let errorElement = null; - let loadingIndicator = null; - - if (this.state.addUserError) { - errorElement = ( - - {this.state.addUserError} - - ); - } - - if (isLoading) { - loadingIndicator = ( - -
    - -
    -
    - ); - } - - return ( -
    { - this.formRef = ref; - }}> - - - - - -
      - {loadingIndicator} - {this.getUserList()} -
    -
    -
    - - - - {errorElement} - - } - placeholder={this.props.intl.formatMessage({ - id: 'auth.username', - defaultMessage: 'Username', - })} - /> - } - placeholder={this.props.intl.formatMessage({ - id: 'auth.password', - defaultMessage: 'Password', - })} - /> - - - - - - - - - - ); - } -} - -const ConnectedAuthTab = connectStores(injectIntl(AuthTab), () => { - return [ - { - store: AuthStore, - event: EventTypes.AUTH_LIST_USERS_SUCCESS, - getValue: ({store}) => { - return { - users: store.getUsers(), - isAdmin: store.isAdmin(), - }; - }, - }, - ]; -}); - -export default ConnectedAuthTab; diff --git a/client/src/javascript/components/modals/settings-modal/AuthTab.tsx b/client/src/javascript/components/modals/settings-modal/AuthTab.tsx new file mode 100644 index 000000000..5e0aeee26 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/AuthTab.tsx @@ -0,0 +1,250 @@ +import classnames from 'classnames'; +import {CSSTransition, TransitionGroup} from 'react-transition-group'; +import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; +import {observer} from 'mobx-react'; +import * as React from 'react'; + +import {AccessLevel} from '@shared/schema/constants/Auth'; +import type {Credentials} from '@shared/schema/Auth'; + +import {Button, Checkbox, Form, FormError, FormRowItem, FormRow, LoadingRing, Textbox} from '../../../ui'; +import AuthActions from '../../../actions/AuthActions'; +import AuthStore from '../../../stores/AuthStore'; +import ClientConnectionSettingsForm from '../../general/connection-settings/ClientConnectionSettingsForm'; +import Close from '../../icons/Close'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; + +import type {ClientConnectionSettingsFormType} from '../../general/connection-settings/ClientConnectionSettingsForm'; + +interface AuthTabFormData { + username: string; + password: string; + isAdmin: boolean; +} + +interface AuthTabStates { + addUserError: string | null; + hasFetchedUserList: boolean; + isAddingUser: boolean; +} + +@observer +class AuthTab extends React.Component { + formData?: Partial; + + formRef?: Form | null = null; + + settingsFormRef: React.RefObject = React.createRef(); + + constructor(props: WrappedComponentProps) { + super(props); + + this.state = { + addUserError: null, + hasFetchedUserList: false, + isAddingUser: false, + }; + } + + componentDidMount() { + if (!AuthStore.currentUser.isAdmin) { + return; + } + + AuthActions.fetchUsers().then(() => { + this.setState({hasFetchedUserList: true}); + }); + } + + handleFormChange = ({formData}: {formData: Record}) => { + this.formData = formData as Partial; + }; + + handleFormSubmit = () => { + if (this.formData == null || this.settingsFormRef.current == null) { + return; + } + + if (this.formData.username == null || this.formData.username === '') { + this.setState({ + addUserError: this.props.intl.formatMessage({ + id: 'auth.error.username.empty', + }), + }); + } else if (this.formData.password == null || this.formData.password === '') { + this.setState({ + addUserError: this.props.intl.formatMessage({ + id: 'auth.error.password.empty', + }), + }); + } else { + this.setState({isAddingUser: true}); + + const connectionSettings = this.settingsFormRef.current.getConnectionSettings(); + if (connectionSettings == null) { + this.setState({ + addUserError: this.props.intl.formatMessage({ + id: 'connection.settings.error.empty', + }), + isAddingUser: false, + }); + return; + } + + AuthActions.createUser({ + username: this.formData.username, + password: this.formData.password, + client: connectionSettings, + level: this.formData.isAdmin === true ? AccessLevel.ADMINISTRATOR : AccessLevel.USER, + }) + .then(AuthActions.fetchUsers, (error) => { + this.setState({ + addUserError: error.response.data.message, + isAddingUser: false, + }); + }) + .then(() => { + if (this.formRef != null) { + this.formRef.resetForm(); + } + this.setState({addUserError: null, isAddingUser: false}); + }); + } + }; + + render() { + const {addUserError, hasFetchedUserList} = this.state; + + if (!AuthStore.currentUser.isAdmin) { + return ( +
    + + + + + + + + +
    + ); + } + + const isLoading = !hasFetchedUserList && AuthStore.users.length === 0; + const interactiveListClasses = classnames('interactive-list', { + 'interactive-list--loading': isLoading, + }); + let errorElement = null; + let loadingIndicator = null; + + if (addUserError) { + errorElement = ( + + {addUserError} + + ); + } + + if (isLoading) { + loadingIndicator = ( + +
    + +
    +
    + ); + } + + return ( +
    { + this.formRef = ref; + }}> + + + + + +
      + {loadingIndicator} + {AuthStore.users + .slice() + .sort((a: Credentials, b: Credentials) => a.username.localeCompare(b.username)) + .map((user: Credentials) => { + const isCurrentUser = user.username === AuthStore.currentUser.username; + let badge = null; + let removeIcon = null; + + if (!isCurrentUser) { + removeIcon = ( + AuthActions.deleteUser(user.username).then(AuthActions.fetchUsers)}> + + + ); + } else { + badge = ( + + + + ); + } + + const classes = classnames('interactive-list__item', { + 'interactive-list__item--disabled': isCurrentUser, + }); + + return ( +
    • + +
      {user.username}
      + {badge} +
      + {removeIcon} +
    • + ); + })} +
    +
    +
    + + + + {errorElement} + + } + placeholder={this.props.intl.formatMessage({ + id: 'auth.username', + })} + autoComplete="username" + /> + } + placeholder={this.props.intl.formatMessage({ + id: 'auth.password', + })} + autoComplete="new-password" + /> + + + + + +

    + + + +

    + ); + } +} + +export default injectIntl(AuthTab); diff --git a/client/src/javascript/components/modals/settings-modal/BandwidthTab.js b/client/src/javascript/components/modals/settings-modal/BandwidthTab.js deleted file mode 100644 index 74b8c1fb2..000000000 --- a/client/src/javascript/components/modals/settings-modal/BandwidthTab.js +++ /dev/null @@ -1,180 +0,0 @@ -import {Form, FormRow, Textbox} from 'flood-ui-kit'; -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import SettingsTab from './SettingsTab'; - -export default class BandwidthTab extends SettingsTab { - state = {}; - - handleFormChange = ({event, formData}) => { - if (event.target.name === 'dropdownPresetDownload' || event.target.name === 'dropdownPresetUpload') { - this.props.onSettingsChange({ - speedLimits: { - download: this.processSpeedsForSave(formData.dropdownPresetDownload), - upload: this.processSpeedsForSave(formData.dropdownPresetUpload), - }, - }); - - return; - } - - this.handleClientSettingFieldChange(event.target.name, event); - }; - - getDownloadValue() { - if (this.props.settings.speedLimits != null) { - return this.processSpeedsForDisplay(this.props.settings.speedLimits.download); - } - - return 0; - } - - getUploadValue() { - if (this.props.settings.speedLimits != null) { - return this.processSpeedsForDisplay(this.props.settings.speedLimits.upload); - } - - return 0; - } - - processSpeedsForDisplay(speeds = []) { - if (!speeds || speeds.length === 0) { - return; - } - - return speeds.map(speed => Number(speed) / 1024).join(', '); - } - - processSpeedsForSave(speeds = '') { - if (speeds === '') { - return []; - } - - return speeds - .replace(/\s/g, '') - .split(',') - .map(speed => Number(speed) * 1024); - } - - render() { - return ( -
    - - - - - - } - id="dropdownPresetDownload" - /> - - - - } - id="dropdownPresetUpload" - /> - - - - } - id="throttleGlobalDownMax" - /> - - } - id="throttleGlobalUpMax" - /> - - - - - - - } - id="throttleMaxUploads" - /> - - } - id="throttleMaxUploadsDiv" - /> - - } - id="throttleMaxUploadsGlobal" - /> - - - - } - id="throttleMaxDownloads" - /> - - } - id="throttleMaxDownloadsDiv" - /> - - } - id="throttleMaxDownloadsGlobal" - /> - -
    - ); - } -} diff --git a/client/src/javascript/components/modals/settings-modal/BandwidthTab.tsx b/client/src/javascript/components/modals/settings-modal/BandwidthTab.tsx new file mode 100644 index 000000000..37044ed25 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/BandwidthTab.tsx @@ -0,0 +1,122 @@ +import {FormattedMessage} from 'react-intl'; +import * as React from 'react'; + +import {Form, FormRow, Textbox} from '../../../ui'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; +import SettingStore from '../../../stores/SettingStore'; +import SettingsTab from './SettingsTab'; + +const processSpeedsForDisplay = (speeds: number[]): string | undefined => { + if (!speeds || speeds.length === 0) { + return undefined; + } + + return speeds.join(', '); +}; + +const processSpeedsForSave = (speeds = ''): number[] => { + if (speeds === '') { + return []; + } + + return speeds + .replace(/\s/g, '') + .split(',') + .map((speed) => Number(speed)); +}; + +export default class BandwidthTab extends SettingsTab { + handleFormChange = ({ + event, + formData, + }: { + event: Event | React.FormEvent; + formData: Record; + }) => { + const inputElement = event.target as HTMLInputElement; + + if (inputElement.name === 'dropdownPresetDownload' || inputElement.name === 'dropdownPresetUpload') { + this.props.onSettingsChange({ + speedLimits: { + download: processSpeedsForSave(formData.dropdownPresetDownload as string), + upload: processSpeedsForSave(formData.dropdownPresetUpload as string), + }, + }); + + return; + } + + this.handleClientSettingChange(event); + }; + + render() { + return ( +
    + + + + + } + id="dropdownPresetDownload" + /> + + + } + id="dropdownPresetUpload" + /> + + + } + id="throttleGlobalDownSpeed" + /> + } + id="throttleGlobalUpSpeed" + /> + + + + + + } + id="throttleMaxUploads" + /> + } + id="throttleMaxUploadsGlobal" + /> + + + } + id="throttleMaxDownloads" + /> + } + id="throttleMaxDownloadsGlobal" + /> + +
    + ); + } +} diff --git a/client/src/javascript/components/modals/settings-modal/ConnectivityTab.js b/client/src/javascript/components/modals/settings-modal/ConnectivityTab.js deleted file mode 100644 index 1ceaf15ce..000000000 --- a/client/src/javascript/components/modals/settings-modal/ConnectivityTab.js +++ /dev/null @@ -1,157 +0,0 @@ -import {Checkbox, Form, FormRow, Textbox} from 'flood-ui-kit'; -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import SettingsTab from './SettingsTab'; - -export default class ConnectivityTab extends SettingsTab { - state = {}; - - getDHTEnabledValue() { - if (this.state.dhtEnabled != null) { - return this.state.dhtEnabled; - } - - return this.props.settings.dhtStats.dht === 'auto'; - } - - handleFormChange = ({event}) => { - if (event.target.name === 'dhtEnabled') { - const dhtEnabled = !this.getDHTEnabledValue(); - const dhtEnabledString = dhtEnabled ? 'auto' : 'disable'; - - this.setState({dhtEnabled}); - this.props.onCustomSettingsChange({ - id: 'dht', - data: [dhtEnabledString], - overrideID: 'dhtStats', - overrideData: {dht: dhtEnabledString}, - }); - } else { - this.handleClientSettingFieldChange(event.target.name, event); - } - }; - - render() { - return ( -
    - - - - - - } - width="one-quarter" - /> - - - - - - - - - - } - /> - - } - /> - - - - - - } - width="one-quarter" - /> - - - - - - - - - - - - } - /> - } - /> - - - - } - /> - - } - /> - - - } - width="one-half" - /> - -
    - ); - } -} diff --git a/client/src/javascript/components/modals/settings-modal/ConnectivityTab.tsx b/client/src/javascript/components/modals/settings-modal/ConnectivityTab.tsx new file mode 100644 index 000000000..7d149e13e --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/ConnectivityTab.tsx @@ -0,0 +1,115 @@ +import {FormattedMessage} from 'react-intl'; + +import {Checkbox, Form, FormRow, Textbox} from '../../../ui'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; +import SettingsTab from './SettingsTab'; + +export default class ConnectivityTab extends SettingsTab { + render() { + return ( +
    this.handleClientSettingChange(event)}> + + + + + } + width="one-quarter" + /> + + + + + + + + + } + /> + } + /> + + + + + + } + width="one-quarter" + /> + + + + + + + + + + + + } + /> + } + /> + + + } + /> + } + /> + + + } + width="one-half" + /> + +
    + ); + } +} diff --git a/client/src/javascript/components/modals/settings-modal/DiskUsageTab.js b/client/src/javascript/components/modals/settings-modal/DiskUsageTab.js deleted file mode 100644 index 0d85dc54c..000000000 --- a/client/src/javascript/components/modals/settings-modal/DiskUsageTab.js +++ /dev/null @@ -1,131 +0,0 @@ -import {Checkbox, Form, FormRow} from 'flood-ui-kit'; -import {FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import SettingsStore from '../../../stores/SettingsStore'; -import SettingsTab from './SettingsTab'; -import SortableList from '../../general/SortableList'; -import DiskUsageStore from '../../../stores/DiskUsageStore'; - -class DiskUsageTab extends SettingsTab { - tooltipRef = null; - - constructor(props) { - super(props); - const mountPoints = SettingsStore.getFloodSettings('mountPoints'); - const disks = DiskUsageStore.getDiskUsage().reduce((disksByTarget, disk) => { - disksByTarget[disk.target] = disk; - return disksByTarget; - }, {}); - - // assemble disk items from saved "mountPoints" and list of disks "disks" - // first targets saved in mountPoints that exist in disks - // then remaing targets from disks - const diskItems = mountPoints - .filter(target => target in disks) - .map(target => ({ - id: target, - visible: true, - })) - .concat( - Object.keys(disks) - .filter(target => !mountPoints.includes(target)) - .map(target => ({ - id: target, - visible: false, - })), - ); - - this.state = { - diskItems, - }; - } - - updateSettings = diskItems => { - const mountPoints = diskItems.filter(item => item.visible).map(item => item.id); - this.props.onSettingsChange({mountPoints}); - }; - - handleDiskCheckboxValueChange = (id, value) => { - const {diskItems} = this.state; - - const newItems = diskItems.map(disk => { - if (disk.id === id) { - return {...disk, visible: value}; - } - return disk; - }); - - this.setState({diskItems: newItems}); - this.updateSettings(newItems); - }; - - handleDiskMouseDown = () => { - if (this.tooltipRef != null) { - this.tooltipRef.dismissTooltip(); - } - }; - - handleDiskMove = items => { - this.setState({diskItems: items}); - this.updateSettings(items); - }; - - renderDiskItem = item => { - const {id, visible} = item; - let checkbox = null; - - if (!item.dragIndicator) { - checkbox = ( - - this.handleDiskCheckboxValueChange(id, event.target.checked)} - modifier="dark"> - - - - ); - } - - const content = ( -
    - -
    {id}
    -
    - {checkbox} -
    - ); - - if (item.dragIndicator) { - return
    {content}
    ; - } - - return content; - }; - - render() { - const {diskItems} = this.state; - - return ( -
    - - - - - - -
    - ); - } -} - -export default injectIntl(DiskUsageTab); diff --git a/client/src/javascript/components/modals/settings-modal/DiskUsageTab.tsx b/client/src/javascript/components/modals/settings-modal/DiskUsageTab.tsx new file mode 100644 index 000000000..7613cc005 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/DiskUsageTab.tsx @@ -0,0 +1,25 @@ +import {FC} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import {Form, FormRow} from '../../../ui'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; +import MountPointsList from './lists/MountPointsList'; + +interface DiskUsageTabProps { + onSettingsChange: (changedSettings: Partial) => void; +} + +const DiskUsageTab: FC = (props: DiskUsageTabProps) => ( +
    + + + + + + +
    +); + +export default DiskUsageTab; diff --git a/client/src/javascript/components/modals/settings-modal/ResourcesTab.js b/client/src/javascript/components/modals/settings-modal/ResourcesTab.js deleted file mode 100644 index e3a2bab1e..000000000 --- a/client/src/javascript/components/modals/settings-modal/ResourcesTab.js +++ /dev/null @@ -1,71 +0,0 @@ -import {Checkbox, Form, FormRow, Textbox} from 'flood-ui-kit'; -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import SettingsTab from './SettingsTab'; - -export default class ResourcesTab extends SettingsTab { - state = {}; - - handleFormChange = ({event}) => { - this.handleClientSettingFieldChange(event.target.name, event); - }; - - render() { - return ( -
    - - - - - - } - /> - - - } - width="one-half" - /> - - - - - - - - - - {' '} - (MB) -
    - } - width="one-half" - /> - - - ); - } -} diff --git a/client/src/javascript/components/modals/settings-modal/ResourcesTab.tsx b/client/src/javascript/components/modals/settings-modal/ResourcesTab.tsx new file mode 100644 index 000000000..07bd0d109 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/ResourcesTab.tsx @@ -0,0 +1,55 @@ +import {FormattedMessage} from 'react-intl'; + +import {Checkbox, Form, FormRow, Textbox} from '../../../ui'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; +import SettingsTab from './SettingsTab'; + +export default class ResourcesTab extends SettingsTab { + render() { + return ( +
    this.handleClientSettingChange(event)}> + + + + + } + /> + + + } + width="one-half" + /> + + + + + + + + + + (MB) +
    + } + width="one-half" + /> + + + ); + } +} diff --git a/client/src/javascript/components/modals/settings-modal/SettingsModal.js b/client/src/javascript/components/modals/settings-modal/SettingsModal.js deleted file mode 100644 index d1ad96900..000000000 --- a/client/src/javascript/components/modals/settings-modal/SettingsModal.js +++ /dev/null @@ -1,246 +0,0 @@ -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import AboutTab from './AboutTab'; -import AuthTab from './AuthTab'; -import BandwidthTab from './BandwidthTab'; -import ConnectivityTab from './ConnectivityTab'; -import connectStores from '../../../util/connectStores'; -import EventTypes from '../../../constants/EventTypes'; -import Modal from '../Modal'; -import ResourcesTab from './ResourcesTab'; -import SettingsStore from '../../../stores/SettingsStore'; -import UITab from './UITab'; -import DiskUsageTab from './DiskUsageTab'; - -class SettingsModal extends React.Component { - state = { - isSavingSettings: false, - changedClientSettings: {}, - changedFloodSettings: {}, - }; - - modalBodyRef = null; - - getActions() { - return [ - { - clickHandler: null, - content: this.props.intl.formatMessage({ - id: 'button.cancel', - defaultMessage: 'Cancel', - }), - triggerDismiss: true, - type: 'tertiary', - }, - { - clickHandler: this.handleSaveSettingsClick, - isLoading: this.state.isSavingSettings, - content: this.props.intl.formatMessage({ - id: 'button.save', - defaultMessage: 'Save Settings', - }), - triggerDismiss: false, - type: 'primary', - }, - ]; - } - - handleCustomsSettingChange = data => { - this.setState(state => { - return { - changedClientSettings: this.mergeObjects(state.changedClientSettings, { - [data.id]: {...data, overrideLocalSetting: true}, - }), - }; - }); - }; - - handleSaveSettingsClick = () => { - const floodSettings = Object.keys(this.state.changedFloodSettings).map(settingsKey => ({ - id: settingsKey, - data: this.state.changedFloodSettings[settingsKey], - })); - - const clientSettings = Object.keys(this.state.changedClientSettings).map(settingsKey => { - const data = this.state.changedClientSettings[settingsKey]; - - if (data.overrideLocalSetting) { - return data; - } - - return {id: settingsKey, data}; - }); - - this.setState({isSavingSettings: true}, () => { - Promise.all([ - SettingsStore.saveFloodSettings(floodSettings, { - dismissModal: true, - alert: true, - }), - SettingsStore.saveClientSettings(clientSettings, { - dismissModal: true, - alert: true, - }), - ]).then(() => { - this.setState({isSavingSettings: false}); - }); - }); - }; - - handleFloodSettingsChange = changedSettings => { - this.setState((state, props) => { - const floodSettings = this.mergeObjects(props.floodSettings, changedSettings); - const changedFloodSettings = this.mergeObjects(state.changedFloodSettings, changedSettings); - - return {floodSettings, changedFloodSettings}; - }); - }; - - handleClientSettingsChange = changedSettings => { - this.setState((state, props) => { - const clientSettings = this.mergeObjects(props.clientSettings, changedSettings); - const changedClientSettings = this.mergeObjects(state.changedClientSettings, changedSettings); - - return {clientSettings, changedClientSettings}; - }); - }; - - handleModalRefSet = (id, ref) => { - if (id === 'modal-body') { - this.modalBodyRef = ref; - } - }; - - // TODO: Use lodash or something non-custom for this - mergeObjects(objA, objB) { - Object.keys(objB).forEach(key => { - if (!Object.prototype.hasOwnProperty.call(objB, key) || objB[key] == null) { - return; - } - - // If it's an object, then recursive merge. - if ( - !Array.isArray(objB[key]) && - !Array.isArray(objB[key]) && - typeof objA[key] === 'object' && - typeof objB[key] === 'object' - ) { - objA[key] = this.mergeObjects(objA[key], objB[key]); - } else { - objA[key] = objB[key]; - } - }); - - return objA; - } - - render() { - const {clientSettings, floodSettings, intl} = this.props; - - const tabs = { - bandwidth: { - content: BandwidthTab, - props: { - onClientSettingsChange: this.handleClientSettingsChange, - onSettingsChange: this.handleFloodSettingsChange, - settings: this.mergeObjects(floodSettings, clientSettings), - }, - label: intl.formatMessage({ - id: 'settings.tabs.bandwidth', - defaultMessage: 'Bandwidth', - }), - }, - connectivity: { - content: ConnectivityTab, - props: { - onCustomSettingsChange: this.handleCustomsSettingChange, - onClientSettingsChange: this.handleClientSettingsChange, - settings: clientSettings, - }, - label: intl.formatMessage({ - id: 'settings.tabs.connectivity', - defaultMessage: 'Connectivity', - }), - }, - resources: { - content: ResourcesTab, - props: { - onClientSettingsChange: this.handleClientSettingsChange, - settings: clientSettings, - }, - label: intl.formatMessage({ - id: 'settings.tabs.resources', - defaultMessage: 'Resources', - }), - }, - authentication: { - content: AuthTab, - label: intl.formatMessage({ - id: 'settings.tabs.authentication', - defaultMessage: 'Authentication', - }), - }, - ui: { - content: UITab, - label: intl.formatMessage({ - id: 'settings.tabs.userinterface', - defaultMessage: 'User Interface', - }), - props: { - onSettingsChange: this.handleFloodSettingsChange, - scrollContainer: this.modalBodyRef, - }, - }, - diskusage: { - content: DiskUsageTab, - label: intl.formatMessage({ - id: 'settings.tabs.diskusage', - defaultMessage: 'Disk Usage', - }), - props: { - onSettingsChange: this.handleFloodSettingsChange, - }, - }, - about: { - content: AboutTab, - label: intl.formatMessage({ - id: 'settings.tabs.about', - defaultMessage: 'About', - }), - }, - }; - - return ( - - ); - } -} - -const ConnectedSettingsModal = connectStores(injectIntl(SettingsModal), () => { - return [ - { - store: SettingsStore, - event: EventTypes.SETTINGS_CHANGE, - getValue: ({store}) => { - return { - clientSettings: store.getClientSettings(), - floodSettings: store.getFloodSettings(), - }; - }, - }, - ]; -}); - -export default ConnectedSettingsModal; diff --git a/client/src/javascript/components/modals/settings-modal/SettingsModal.tsx b/client/src/javascript/components/modals/settings-modal/SettingsModal.tsx new file mode 100644 index 000000000..91bb12766 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/SettingsModal.tsx @@ -0,0 +1,152 @@ +import {FC, useState} from 'react'; +import {useIntl} from 'react-intl'; +import {useMedia} from 'react-use'; + +import type {ClientSettings} from '@shared/types/ClientSettings'; +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import AboutTab from './AboutTab'; +import AuthTab from './AuthTab'; +import BandwidthTab from './BandwidthTab'; +import ConfigStore from '../../../stores/ConfigStore'; +import ConnectivityTab from './ConnectivityTab'; +import ClientActions from '../../../actions/ClientActions'; +import DiskUsageTab from './DiskUsageTab'; +import Modal from '../Modal'; +import ResourcesTab from './ResourcesTab'; +import SettingActions from '../../../actions/SettingActions'; +import UITab from './UITab'; +import UIStore from '../../../stores/UIStore'; + +const SettingsModal: FC = () => { + const intl = useIntl(); + const isSmallScreen = useMedia('(max-width: 720px)'); + + const [changedClientSettings, setChangedClientSettings] = useState>({}); + const [changedFloodSettings, setChangedFloodSettings] = useState>({}); + const [isSavingSettings, setSavingSettings] = useState(false); + + const handleClientSettingsChange = (changedSettings: Partial) => { + setChangedClientSettings({ + ...changedClientSettings, + ...changedSettings, + }); + }; + + const handleFloodSettingsChange = (changedSettings: Partial) => { + setChangedFloodSettings({ + ...changedFloodSettings, + ...changedSettings, + }); + }; + + const tabs = { + bandwidth: { + content: BandwidthTab, + props: { + onClientSettingsChange: handleClientSettingsChange, + onSettingsChange: handleFloodSettingsChange, + }, + label: intl.formatMessage({ + id: 'settings.tabs.bandwidth', + }), + }, + connectivity: { + content: ConnectivityTab, + props: { + onClientSettingsChange: handleClientSettingsChange, + }, + label: intl.formatMessage({ + id: 'settings.tabs.connectivity', + }), + }, + resources: { + content: ResourcesTab, + props: { + onClientSettingsChange: handleClientSettingsChange, + }, + label: intl.formatMessage({ + id: 'settings.tabs.resources', + }), + }, + ...(ConfigStore.authMethod !== 'none' + ? { + authentication: { + content: AuthTab, + label: intl.formatMessage({ + id: 'settings.tabs.authentication', + }), + }, + } + : {}), + ui: { + content: UITab, + label: intl.formatMessage({ + id: 'settings.tabs.userinterface', + }), + props: { + onSettingsChange: handleFloodSettingsChange, + }, + }, + diskusage: { + content: DiskUsageTab, + label: intl.formatMessage({ + id: 'settings.tabs.diskusage', + }), + props: { + onSettingsChange: handleFloodSettingsChange, + }, + }, + about: { + content: AboutTab, + label: intl.formatMessage({ + id: 'settings.tabs.about', + }), + }, + }; + + return ( + { + setSavingSettings(true); + Promise.all([ + SettingActions.saveSettings(changedFloodSettings, { + alert: true, + }), + ClientActions.saveSettings(changedClientSettings, { + alert: true, + }), + ]).then(() => { + setSavingSettings(false); + UIStore.dismissModal(); + }); + }, + isLoading: isSavingSettings, + content: intl.formatMessage({ + id: 'button.save', + }), + triggerDismiss: false, + type: 'primary', + }, + ]} + size="large" + heading={intl.formatMessage({ + id: 'settings.tabs.heading', + })} + orientation={isSmallScreen ? 'horizontal' : 'vertical'} + tabs={tabs} + /> + ); +}; + +export default SettingsModal; diff --git a/client/src/javascript/components/modals/settings-modal/SettingsTab.js b/client/src/javascript/components/modals/settings-modal/SettingsTab.js deleted file mode 100644 index b4727abad..000000000 --- a/client/src/javascript/components/modals/settings-modal/SettingsTab.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -export default class SettingsTab extends React.Component { - state = {}; - - getFieldValue(fieldName) { - if (this.state[fieldName] == null) { - return this.props.settings[fieldName] || ''; - } - - return this.state[fieldName]; - } - - handleClientSettingFieldChange(fieldName, event) { - let {value} = event.target; - - if (event.target.type === 'checkbox') { - value = event.target.checked ? '1' : '0'; - } - - const nextState = {[fieldName]: value}; - - this.setState(nextState); - this.props.onClientSettingsChange(nextState); - } -} diff --git a/client/src/javascript/components/modals/settings-modal/SettingsTab.tsx b/client/src/javascript/components/modals/settings-modal/SettingsTab.tsx new file mode 100644 index 000000000..de5ed651f --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/SettingsTab.tsx @@ -0,0 +1,57 @@ +import {WrappedComponentProps} from 'react-intl'; +import * as React from 'react'; + +import type {ClientSetting, ClientSettings} from '@shared/types/ClientSettings'; +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import SettingStore from '../../../stores/SettingStore'; + +interface SettingsTabProps extends WrappedComponentProps { + onSettingsChange: (changeSettings: Partial) => void; + onClientSettingsChange: (changeSettings: Partial) => void; +} + +interface SettingsTabStates { + changedClientSettings: Partial; +} + +class SettingsTab extends React.Component { + constructor(props: SettingsTabProps) { + super(props); + + this.state = { + changedClientSettings: {}, + }; + } + + getChangedClientSetting(property: T): ClientSettings[T] | undefined { + if (this.state.changedClientSettings[property] != null) { + return this.state.changedClientSettings[property] as ClientSettings[T]; + } + + return SettingStore.clientSettings?.[property]; + } + + handleClientSettingChange(event: React.FormEvent | Event) { + const inputElement = event.target as HTMLInputElement; + const property = inputElement.name as ClientSetting; + const {value, type, checked} = inputElement; + + let changedClientSetting: Partial = {}; + if (type === 'checkbox') { + changedClientSetting = {[property]: checked}; + } else { + changedClientSetting = {[property]: value}; + } + + this.setState((prev) => ({ + changedClientSettings: { + ...prev.changedClientSettings, + changedClientSetting, + }, + })); + this.props.onClientSettingsChange(changedClientSetting); + } +} + +export default SettingsTab; diff --git a/client/src/javascript/components/modals/settings-modal/UITab.js b/client/src/javascript/components/modals/settings-modal/UITab.js deleted file mode 100644 index bc642797c..000000000 --- a/client/src/javascript/components/modals/settings-modal/UITab.js +++ /dev/null @@ -1,221 +0,0 @@ -import {Checkbox, Form, FormRow, Select, SelectItem, Radio} from 'flood-ui-kit'; -import {FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import ErrorIcon from '../../icons/ErrorIcon'; -import Languages from '../../../constants/Languages'; -import ModalFormSectionHeader from '../ModalFormSectionHeader'; -import SettingsStore from '../../../stores/SettingsStore'; -import SettingsTab from './SettingsTab'; -import SortableList from '../../general/SortableList'; -import Tooltip from '../../general/Tooltip'; -import TorrentProperties from '../../../constants/TorrentProperties'; - -class UITab extends SettingsTab { - tooltipRef = null; - - state = { - torrentDetails: SettingsStore.getFloodSettings('torrentDetails'), - torrentListViewSize: SettingsStore.getFloodSettings('torrentListViewSize'), - selectedLanguage: SettingsStore.getFloodSettings('language'), - }; - - getLanguageSelectOptions() { - return Object.keys(Languages).map(languageID => { - const selectedLanguageDefinition = Languages[languageID]; - - return ( - - {this.props.intl.formatMessage(selectedLanguageDefinition)} - - ); - }); - } - - getLockedIDs() { - if (this.state.torrentListViewSize === 'expanded') { - return ['name', 'eta', 'downloadRate', 'uploadRate']; - } - - return []; - } - - handleDetailCheckboxValueChange = (id, value) => { - let {torrentDetails} = this.state; - - torrentDetails = torrentDetails.map(detail => { - if (detail.id === id) { - detail.visible = value; - } - - return detail; - }); - - this.props.onSettingsChange({torrentDetails}); - this.setState({torrentDetails}); - }; - - handleFormChange = ({event, formData}) => { - if (event.target.type === 'radio') { - const newState = {torrentListViewSize: formData['ui-torrent-size']}; - - this.props.onSettingsChange(newState); - this.setState(newState); - } - - if (event.target.name === 'language') { - const {language} = formData; - - this.setState({selectedLanguage: language}); - this.props.onSettingsChange({language}); - } - }; - - handleTorrentDetailsMouseDown = () => { - if (this.tooltipRef != null) { - this.tooltipRef.dismissTooltip(); - } - }; - - handleTorrentDetailsMove = items => { - this.setState({torrentDetails: items}); - this.props.onSettingsChange({torrentDetails: items}); - }; - - renderTorrentDetailItem = (item, index) => { - const {id, visible} = item; - let checkbox = null; - let warning = null; - - if (!item.dragIndicator && !this.getLockedIDs().includes(id)) { - checkbox = ( - - this.handleDetailCheckboxValueChange(id, event.target.checked)} - modifier="dark"> - - - - ); - } - - if ( - id === 'tags' && - this.state.torrentListViewSize === 'expanded' && - index < this.state.torrentDetails.length - 1 - ) { - const tooltipContent = ( - - ); - - warning = ( - { - this.tooltipRef = ref; - }} - scrollContainer={this.props.scrollContainer} - width={200} - wrapperClassName="sortable-list__content sortable-list__content--secondary tooltip__wrapper" - wrapText> - - - ); - } - - const content = ( -
    - {warning} - - - - {checkbox} -
    - ); - - if (item.dragIndicator) { - return
    {content}
    ; - } - - return content; - }; - - render() { - const lockedIDs = this.getLockedIDs(); - let torrentDetailItems = this.state.torrentDetails.slice(); - - if (this.state.torrentListViewSize === 'expanded') { - let nextUnlockedIndex = lockedIDs.length; - - torrentDetailItems = torrentDetailItems - .reduce((accumulator, detail) => { - const lockedIDIndex = lockedIDs.indexOf(detail.id); - - if (lockedIDIndex > -1) { - accumulator[lockedIDIndex] = detail; - } else { - accumulator[nextUnlockedIndex++] = detail; - } - - return accumulator; - }, []) - .filter(item => item != null); - } - - return ( -
    - - - - - - - - - - - - - - - - - - - - - - - -
    - ); - } -} - -export default injectIntl(UITab); diff --git a/client/src/javascript/components/modals/settings-modal/UITab.tsx b/client/src/javascript/components/modals/settings-modal/UITab.tsx new file mode 100644 index 000000000..e8f6e3c5e --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/UITab.tsx @@ -0,0 +1,130 @@ +import {FormattedMessage, injectIntl} from 'react-intl'; +import * as React from 'react'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import {Form, FormRow, Select, SelectItem, Radio} from '../../../ui'; +import Languages from '../../../constants/Languages'; +import ModalFormSectionHeader from '../ModalFormSectionHeader'; +import SettingStore from '../../../stores/SettingStore'; +import SettingsTab from './SettingsTab'; +import TorrentContextMenuActionsList from './lists/TorrentContextMenuActionsList'; +import TorrentListColumnsList from './lists/TorrentListColumnsList'; + +import type {Language} from '../../../constants/Languages'; + +class UITab extends SettingsTab { + torrentListViewSize = SettingStore.floodSettings.torrentListViewSize; + selectedLanguage = SettingStore.floodSettings.language; + UITagSelectorMode = SettingStore.floodSettings.UITagSelectorMode; + + getLanguageSelectOptions() { + return Object.keys(Languages).map((languageID) => ( + + {Languages[languageID as 'auto'].id != null + ? this.props.intl.formatMessage({ + id: Languages[languageID as 'auto'].id, + }) + : Languages[languageID as Language]} + + )); + } + + handleFormChange = ({ + event, + formData, + }: { + event: Event | React.FormEvent; + formData: Record; + }) => { + const inputElement = event.target as HTMLInputElement; + + if (inputElement.type === 'radio') { + this.torrentListViewSize = formData['ui-torrent-size'] as FloodSettings['torrentListViewSize']; + this.UITagSelectorMode = formData['ui-tag-selector-mode'] as FloodSettings['UITagSelectorMode']; + this.props.onSettingsChange({ + torrentListViewSize: this.torrentListViewSize, + UITagSelectorMode: this.UITagSelectorMode, + }); + } + + if (inputElement.name === 'language') { + this.selectedLanguage = formData.language as FloodSettings['language']; + if (this.selectedLanguage === 'translate') { + SettingStore.saveFloodSettings({language: 'translate'}); + } else { + this.props.onSettingsChange({language: this.selectedLanguage}); + } + } + }; + + render() { + return ( +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + ); + } +} + +export default injectIntl(UITab); diff --git a/client/src/javascript/components/modals/settings-modal/UITabSortableDetailColumns.js b/client/src/javascript/components/modals/settings-modal/UITabSortableDetailColumns.js deleted file mode 100644 index 4cbc242cc..000000000 --- a/client/src/javascript/components/modals/settings-modal/UITabSortableDetailColumns.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; - -import SortableList from '../../general/SortableList'; - -class UITabSortableDetailColumns extends React.PureComponent { - getLockedIDs() { - if (this.props.torrentListViewSize === 'expanded') { - return ['name', 'eta', 'downloadRate', 'uploadRate']; - } - - return []; - } - - render() { - const lockedIDs = this.getLockedIDs(); - let torrentDetailItems = this.props.torrentDetails.slice(); - - if (this.props.torrentListViewSize === 'expanded') { - let nextUnlockedIndex = lockedIDs.length; - - torrentDetailItems = torrentDetailItems.reduce((accumulator, detail) => { - const lockedIDIndex = lockedIDs.indexOf(detail.id); - - if (lockedIDIndex > -1) { - accumulator[lockedIDIndex] = detail; - } else { - accumulator[nextUnlockedIndex++] = detail; - } - - return accumulator; - }, []); - } - - return ( - - ); - } -} - -export default UITabSortableDetailColumns; diff --git a/client/src/javascript/components/modals/settings-modal/lists/MountPointsList.tsx b/client/src/javascript/components/modals/settings-modal/lists/MountPointsList.tsx new file mode 100644 index 000000000..de7276727 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/lists/MountPointsList.tsx @@ -0,0 +1,125 @@ +import {Component} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import {Checkbox} from '../../../../ui'; +import DiskUsageStore from '../../../../stores/DiskUsageStore'; +import SettingStore from '../../../../stores/SettingStore'; +import SortableList, {ListItem} from '../../../general/SortableList'; + +interface MountPointsListProps { + onSettingsChange: (changedSettings: Partial) => void; +} + +interface MountPointsListStates { + diskItems: Array; +} + +class MountPointsList extends Component { + constructor(props: MountPointsListProps) { + super(props); + + const {mountPoints} = SettingStore.floodSettings; + const disks = Object.assign( + {}, + ...DiskUsageStore.disks.map((disk) => ({ + [disk.target]: disk, + })), + ); + + // assemble disk items from saved "mountPoints" and list of disks "disks" + // first targets saved in mountPoints that exist in disks + // then remaining targets from disks + const diskItems = mountPoints + .filter((target) => target in disks) + .map((target) => ({ + id: target, + visible: true, + })) + .concat( + Object.keys(disks) + .filter((target) => !mountPoints.includes(target)) + .map((target) => ({ + id: target, + visible: false, + })), + ); + + this.state = { + diskItems, + }; + } + + updateSettings = (diskItems: Array) => { + this.props.onSettingsChange({ + mountPoints: diskItems.filter((item) => item.visible).map((item) => item.id), + }); + }; + + handleCheckboxValueChange = (id: string, value: boolean) => { + const {diskItems} = this.state; + + const newItems = diskItems.map((disk) => { + if (disk.id === id) { + return {...disk, visible: value}; + } + return disk; + }); + + this.setState({diskItems: newItems}); + this.updateSettings(newItems); + }; + + handleMouseDown = () => { + // do nothing. + }; + + handleMove = (items: Array): void => { + this.setState({diskItems: items}); + this.updateSettings(items); + }; + + renderItem = (item: ListItem) => { + const {id, visible} = item; + + const checkbox = ( + + this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}> + + + + ); + + const content = ( +
    + +
    {id}
    +
    + {checkbox} +
    + ); + + return content; + }; + + render() { + const {diskItems} = this.state; + + return ( + + ); + } +} + +export default MountPointsList; diff --git a/client/src/javascript/components/modals/settings-modal/lists/TorrentContextMenuActionsList.tsx b/client/src/javascript/components/modals/settings-modal/lists/TorrentContextMenuActionsList.tsx new file mode 100644 index 000000000..93369d524 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/lists/TorrentContextMenuActionsList.tsx @@ -0,0 +1,113 @@ +import {Component} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import {Checkbox} from '../../../../ui'; +import SettingStore from '../../../../stores/SettingStore'; +import SortableList, {ListItem} from '../../../general/SortableList'; +import Tooltip from '../../../general/Tooltip'; +import TorrentContextMenuActions from '../../../../constants/TorrentContextMenuActions'; + +import type {TorrentContextMenuAction} from '../../../../constants/TorrentContextMenuActions'; + +interface TorrentContextMenuActionsListProps { + onSettingsChange: (changedSettings: Partial) => void; +} + +interface TorrentContextMenuActionsListStates { + torrentContextMenuActions: FloodSettings['torrentContextMenuActions']; +} + +const lockedIDs: Array = ['start', 'stop', 'setTaxonomy', 'torrentDetails']; + +class TorrentContextMenuActionsList extends Component< + TorrentContextMenuActionsListProps, + TorrentContextMenuActionsListStates +> { + tooltipRef: Tooltip | null = null; + + constructor(props: TorrentContextMenuActionsListProps) { + super(props); + + const {torrentContextMenuActions} = SettingStore.floodSettings; + + this.state = { + torrentContextMenuActions: Object.keys(TorrentContextMenuActions).map((key) => ({ + id: key, + visible: torrentContextMenuActions.some((setting) => setting.id === key && setting.visible), + })) as FloodSettings['torrentContextMenuActions'], + }; + } + + updateSettings = (torrentContextMenuActions: FloodSettings['torrentContextMenuActions']) => { + this.props.onSettingsChange({torrentContextMenuActions}); + }; + + handleCheckboxValueChange = (id: string, value: boolean) => { + let {torrentContextMenuActions} = this.state; + + torrentContextMenuActions = torrentContextMenuActions.map((setting) => ({ + id: setting.id, + visible: setting.id === id ? value : setting.visible, + })); + + this.props.onSettingsChange({torrentContextMenuActions}); + this.setState({torrentContextMenuActions}); + }; + + handleMouseDown = () => { + if (this.tooltipRef != null) { + this.tooltipRef.dismissTooltip(); + } + }; + + handleMove = () => { + // do nothing. + }; + + renderItem = (item: ListItem) => { + const {id, visible} = item as FloodSettings['torrentContextMenuActions'][number]; + let checkbox = null; + + if (!lockedIDs.includes(id)) { + checkbox = ( + + this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}> + + + + ); + } + + const content = ( +
    + + + + {checkbox} +
    + ); + + return content; + }; + + render() { + return ( + + ); + } +} + +export default TorrentContextMenuActionsList; diff --git a/client/src/javascript/components/modals/settings-modal/lists/TorrentListColumnsList.tsx b/client/src/javascript/components/modals/settings-modal/lists/TorrentListColumnsList.tsx new file mode 100644 index 000000000..3e1ee4816 --- /dev/null +++ b/client/src/javascript/components/modals/settings-modal/lists/TorrentListColumnsList.tsx @@ -0,0 +1,155 @@ +import {FormattedMessage} from 'react-intl'; +import * as React from 'react'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import {Checkbox} from '../../../../ui'; +import ErrorIcon from '../../../icons/ErrorIcon'; +import SettingStore from '../../../../stores/SettingStore'; +import SortableList from '../../../general/SortableList'; +import Tooltip from '../../../general/Tooltip'; +import TorrentListColumns from '../../../../constants/TorrentListColumns'; + +import type {ListItem} from '../../../general/SortableList'; +import type {TorrentListColumn} from '../../../../constants/TorrentListColumns'; + +interface TorrentListColumnsListProps { + torrentListViewSize: FloodSettings['torrentListViewSize']; + onSettingsChange: (changedSettings: Partial) => void; +} + +interface TorrentListColumnsListStates { + torrentListColumns: FloodSettings['torrentListColumns']; +} + +class TorrentListColumnsList extends React.Component { + tooltipRef: Tooltip | null = null; + + constructor(props: TorrentListColumnsListProps) { + super(props); + + const {torrentListColumns} = SettingStore.floodSettings; + + const torrentListColumnItems: ListItem[] = torrentListColumns + .filter((column) => TorrentListColumns[column.id] != null) + .slice(); + + const newTorrentListColumnItems: ListItem[] = Object.keys(TorrentListColumns) + .filter((key) => torrentListColumns.every((column) => column.id !== key)) + .map((newColumn) => ({ + id: newColumn, + visible: false, + })); + + this.state = { + torrentListColumns: torrentListColumnItems.concat( + newTorrentListColumnItems, + ) as FloodSettings['torrentListColumns'], + }; + } + + getLockedIDs(): Array { + if (this.props.torrentListViewSize === 'expanded') { + return ['name', 'eta', 'downRate', 'percentComplete', 'downTotal', 'upRate']; + } + + return []; + } + + handleCheckboxValueChange = (id: string, value: boolean): void => { + const {torrentListColumns} = this.state; + + const changedTorrentListColumns = torrentListColumns.map((column) => ({ + id: column.id, + visible: column.id === id ? value : column.visible, + })); + + this.props.onSettingsChange({ + torrentListColumns: changedTorrentListColumns, + }); + this.setState({torrentListColumns: changedTorrentListColumns}); + }; + + handleMouseDown = (): void => { + if (this.tooltipRef != null) { + this.tooltipRef.dismissTooltip(); + } + }; + + handleMove = (items: Array): void => { + const changedItems = items.slice() as FloodSettings['torrentListColumns']; + this.setState({torrentListColumns: changedItems}); + this.props.onSettingsChange({torrentListColumns: changedItems}); + }; + + renderItem = (item: ListItem, index: number): React.ReactNode => { + const {id, visible} = item as FloodSettings['torrentListColumns'][number]; + let checkbox = null; + let warning = null; + + if (!this.getLockedIDs().includes(id)) { + checkbox = ( + + this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}> + + + + ); + } + + if ( + id === 'tags' && + this.props.torrentListViewSize === 'expanded' && + index < this.state.torrentListColumns.length - 1 + ) { + const tooltipContent = ; + + warning = ( + { + this.tooltipRef = ref; + }} + width={200} + wrapperClassName="sortable-list__content sortable-list__content--secondary tooltip__wrapper" + wrapText> + + + ); + } + + const content = ( +
    + {warning} + + + + {checkbox} +
    + ); + + return content; + }; + + render(): React.ReactNode { + const lockedIDs = this.getLockedIDs(); + + return ( + + ); + } +} + +export default TorrentListColumnsList; diff --git a/client/src/javascript/components/modals/torrent-details-modal/NavigationList.js b/client/src/javascript/components/modals/torrent-details-modal/NavigationList.js deleted file mode 100644 index fba6fb2a2..000000000 --- a/client/src/javascript/components/modals/torrent-details-modal/NavigationList.js +++ /dev/null @@ -1,54 +0,0 @@ -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -class NavigationList extends React.Component { - static propTypes = { - itemClassName: PropTypes.string, - items: PropTypes.array, - listClassName: PropTypes.string, - onChange: PropTypes.func, - selectedClassName: PropTypes.string, - }; - - static defaultProps = { - itemClassName: 'navigation__item', - listClassName: 'navigation', - }; - - constructor() { - super(); - - this.state = { - selectedItem: null, - }; - } - - getNavigationItems(items) { - return items.map(item => { - const classes = classnames(this.props.itemClassName, { - [this.props.selectedClassName]: item.slug === this.state.selectedItem, - }); - - return ( -
  • - {item.label} -
  • - ); - }); - } - - handleItemClick(item) { - this.setState({ - selectedItem: item.slug, - }); - - this.props.onChange(item); - } - - render() { - return
      {this.getNavigationItems(this.props.items)}
    ; - } -} - -export default NavigationList; diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentContents.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentContents.tsx new file mode 100644 index 000000000..c0f10328d --- /dev/null +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentContents.tsx @@ -0,0 +1,213 @@ +import {observable, runInAction} from 'mobx'; +import classnames from 'classnames'; +import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; +import {observer} from 'mobx-react'; +import * as React from 'react'; + +import type {TorrentContent, TorrentContentSelection, TorrentContentSelectionTree} from '@shared/types/TorrentContent'; + +import {Button, Checkbox, Form, FormRow, FormRowItem, Select, SelectItem} from '../../../ui'; +import ConfigStore from '../../../stores/ConfigStore'; +import Disk from '../../icons/Disk'; +import DirectoryTree from '../../general/filesystem/DirectoryTree'; +import selectionTree from '../../../util/selectionTree'; +import TorrentActions from '../../../actions/TorrentActions'; +import TorrentStore from '../../../stores/TorrentStore'; +import UIStore from '../../../stores/UIStore'; + +@observer +class TorrentContents extends React.Component { + contents = observable.array([]); + itemsTree = observable.object({}); + selectedIndices = observable.array([]); + polling = setInterval(() => { + // TODO: itemsTree is not regenerated as that would override user's selection. + // As a result, percentage of contents of an active torrent is not updated. + // this.fetchTorrentContents(); + }, ConfigStore.pollInterval); + + constructor(props: WrappedComponentProps) { + super(props); + + this.fetchTorrentContents(true); + } + + componentWillUnmount() { + clearInterval(this.polling); + } + + fetchTorrentContents = (populateTree = false) => { + if (UIStore.activeModal?.id === 'torrent-details') { + TorrentActions.fetchTorrentContents(UIStore.activeModal?.hash).then((contents) => { + if (contents != null) { + runInAction(() => { + this.contents.replace(contents); + if (populateTree) { + this.itemsTree = selectionTree.getSelectionTree(this.contents); + } + }); + } + }); + } + }; + + handleDownloadButtonClick = (hash: string, event: React.MouseEvent): void => { + event.preventDefault(); + const {baseURI} = ConfigStore; + const link = document.createElement('a'); + + link.download = ''; + link.href = `${baseURI}api/torrents/${hash}/contents/${this.selectedIndices.join(',')}/data`; + link.style.display = 'none'; + + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + + handleFormChange = (hash: string, {event}: {event: Event | React.FormEvent}): void => { + if (event.target != null && (event.target as HTMLInputElement).name === 'file-priority') { + const inputElement = event.target as HTMLInputElement; + if (inputElement.name === 'file-priority') { + TorrentActions.setFilePriority(hash, { + indices: this.selectedIndices, + priority: Number(inputElement.value), + }); + } + } + }; + + handleItemSelect = (selectedItem: TorrentContentSelection) => { + runInAction(() => { + this.itemsTree = selectionTree.applySelection(this.itemsTree, selectedItem); + this.selectedIndices.replace(selectionTree.getSelectedItems(this.itemsTree)); + }); + }; + + handleSelectAllClick = () => { + runInAction(() => { + this.itemsTree = selectionTree.getSelectionTree( + this.contents, + this.selectedIndices.length < this.contents.length, + ); + this.selectedIndices.replace(selectionTree.getSelectedItems(this.itemsTree)); + }); + }; + + render() { + if (UIStore.activeModal?.id !== 'torrent-details') { + return null; + } + + const {hash} = UIStore?.activeModal; + + let directoryHeadingIconContent = null; + let fileDetailContent = null; + + let allSelected = false; + if (this.contents?.length > 0) { + allSelected = this.selectedIndices.length >= this.contents.length; + directoryHeadingIconContent = ( +
    +
    + + + +
    +
    + +
    +
    + ); + fileDetailContent = ( + + ); + } else { + directoryHeadingIconContent = ; + fileDetailContent = ( +
    + +
    + ); + } + + const directoryHeadingClasses = classnames( + 'directory-tree__node', + 'directory-tree__parent-directory torrent-details__section__heading', + { + 'directory-tree__node--selected': allSelected, + }, + ); + + const directoryHeading = ( +
    +
    + {directoryHeadingIconContent} +
    {TorrentStore.torrents?.[hash].directory}
    +
    +
    + ); + + const wrapperClasses = classnames('inverse directory-tree__wrapper', { + 'directory-tree__wrapper--toolbar-visible': this.selectedIndices.length > 0, + }); + + return ( +
    this.handleFormChange(hash, e)}> +
    + + + {this.selectedIndices.length} + ), + }} + /> + + + + +
    +
    + {directoryHeading} + {fileDetailContent} +
    +
    + ); + } +} + +export default injectIntl(TorrentContents); diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentDetailsModal.js b/client/src/javascript/components/modals/torrent-details-modal/TorrentDetailsModal.js deleted file mode 100644 index 8ced8881f..000000000 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentDetailsModal.js +++ /dev/null @@ -1,121 +0,0 @@ -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import connectStores from '../../../util/connectStores'; -import Modal from '../Modal'; -import EventTypes from '../../../constants/EventTypes'; -import TorrentMediainfo from './TorrentMediainfo'; -import TorrentFiles from './TorrentFiles'; -import TorrentGeneralInfo from './TorrentGeneralInfo'; -import TorrentHeading from './TorrentHeading'; -import TorrentPeers from './TorrentPeers'; -import TorrentStore from '../../../stores/TorrentStore'; -import TorrentTrackers from './TorrentTrackers'; -import UIActions from '../../../actions/UIActions'; -import UIStore from '../../../stores/UIStore'; - -class TorrentDetailsModal extends React.Component { - componentDidMount() { - TorrentStore.fetchTorrentDetails(); - } - - componentWillUnmount() { - TorrentStore.stopPollingTorrentDetails(); - } - - dismissModal() { - UIActions.dismissModal(); - } - - getModalHeading() { - return ; - } - - render() { - const props = { - ...this.props.options, - torrent: this.props.torrent, - ...this.props.torrentDetails, - }; - - const tabs = { - 'torrent-details': { - content: TorrentGeneralInfo, - label: this.props.intl.formatMessage({ - id: 'torrents.details.details', - defaultMessage: 'Details', - }), - props, - }, - 'torrent-files': { - content: TorrentFiles, - label: this.props.intl.formatMessage({ - id: 'torrents.details.files', - defaultMessage: 'Files', - }), - modalContentClasses: 'modal__content--nested-scroll', - props, - }, - 'torrent-peers': { - content: TorrentPeers, - label: this.props.intl.formatMessage({ - id: 'torrents.details.peers', - defaultMessage: 'Peers', - }), - props, - }, - 'torrent-trackers': { - content: TorrentTrackers, - label: this.props.intl.formatMessage({ - id: 'torrents.details.trackers', - defaultMessage: 'Trackers', - }), - props, - }, - 'torrent-mediainfo': { - content: TorrentMediainfo, - label: this.props.intl.formatMessage({ - id: 'torrents.details.mediainfo', - defaultMessage: 'Mediainfo', - }), - props, - }, - }; - - return ( - - ); - } -} - -const ConnectedTorrentDetailsModal = connectStores(injectIntl(TorrentDetailsModal), () => { - return [ - { - store: TorrentStore, - event: EventTypes.CLIENT_TORRENT_DETAILS_CHANGE, - getValue: ({store}) => { - return { - torrentDetails: store.getTorrentDetails(UIStore.getTorrentDetailsHash()), - }; - }, - }, - { - store: TorrentStore, - event: EventTypes.CLIENT_TORRENTS_REQUEST_SUCCESS, - getValue: ({store}) => { - return { - torrent: store.getTorrent(UIStore.getTorrentDetailsHash()), - }; - }, - }, - ]; -}); - -export default ConnectedTorrentDetailsModal; diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentDetailsModal.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentDetailsModal.tsx new file mode 100644 index 000000000..93512b53a --- /dev/null +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentDetailsModal.tsx @@ -0,0 +1,63 @@ +import {FC} from 'react'; +import {useIntl} from 'react-intl'; +import {useMedia} from 'react-use'; + +import Modal from '../Modal'; +import TorrentMediainfo from './TorrentMediainfo'; +import TorrentContents from './TorrentContents'; +import TorrentGeneralInfo from './TorrentGeneralInfo'; +import TorrentHeading from './TorrentHeading'; +import TorrentPeers from './TorrentPeers'; +import TorrentTrackers from './TorrentTrackers'; + +const TorrentDetailsModal: FC = () => { + const intl = useIntl(); + const isSmallScreen = useMedia('(max-width: 720px)'); + + const tabs = { + 'torrent-details': { + content: TorrentGeneralInfo, + label: intl.formatMessage({ + id: 'torrents.details.details', + }), + }, + 'torrent-contents': { + content: TorrentContents, + label: intl.formatMessage({ + id: 'torrents.details.files', + }), + modalContentClasses: 'modal__content--nested-scroll', + }, + 'torrent-peers': { + content: TorrentPeers, + label: intl.formatMessage({ + id: 'torrents.details.peers', + }), + }, + 'torrent-trackers': { + content: TorrentTrackers, + label: intl.formatMessage({ + id: 'torrents.details.trackers', + }), + }, + 'torrent-mediainfo': { + content: TorrentMediainfo, + label: intl.formatMessage({ + id: 'torrents.details.mediainfo', + }), + modalContentClasses: 'modal__content--nested-scroll', + }, + }; + + return ( + } + size="large" + tabs={tabs} + orientation={isSmallScreen ? 'horizontal' : 'vertical'} + tabsInBody={!isSmallScreen} + /> + ); +}; + +export default TorrentDetailsModal; diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentFiles.js b/client/src/javascript/components/modals/torrent-details-modal/TorrentFiles.js deleted file mode 100644 index ebd78d732..000000000 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentFiles.js +++ /dev/null @@ -1,359 +0,0 @@ -import _ from 'lodash'; -import {Button, Checkbox, Form, FormRow, FormRowItem, Select, SelectItem} from 'flood-ui-kit'; -import classnames from 'classnames'; -import {FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import ConfigStore from '../../../stores/ConfigStore'; -import Disk from '../../icons/Disk'; -import DirectoryTree from '../../general/filesystem/DirectoryTree'; -import TorrentActions from '../../../actions/TorrentActions'; - -const TORRENT_PROPS_TO_CHECK = ['bytesDone']; -const METHODS_TO_BIND = ['handleItemSelect', 'handlePriorityChange', 'handleSelectAllClick']; - -class TorrentFiles extends React.Component { - constructor() { - super(); - - this.hasSelectionChanged = false; - this.hasPriorityChanged = false; - - this.state = { - allSelected: false, - selectedItems: {}, - selectedFiles: [], - }; - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - shouldComponentUpdate(nextProps) { - if (this.hasSelectionChanged) { - this.hasSelectionChanged = false; - return true; - } - - // If we know that the user changed a file's priority, we deeply check the - // file tree to render when the priority change is detected. - if (this.hasPriorityChanged) { - const shouldUpdate = !_.isEqual(nextProps.fileTree, this.props.fileTree); - - // Reset the flag so we don't deeply check the next file tree. - if (shouldUpdate) { - this.hasPriorityChanged = false; - } - - return shouldUpdate; - } - - // Update when the previous props weren't defined and the next are. - if ((!this.props.torrent && nextProps.torrent) || (!this.props.fileTree && nextProps.fileTree)) { - return true; - } - - // Check specific properties to re-render when the torrent is active. - if (nextProps.torrent) { - return TORRENT_PROPS_TO_CHECK.some(property => this.props.torrent[property] !== nextProps.torrent[property]); - } - - return true; - } - - getSelectedFiles(selectionTree, selectedFiles = []) { - if (selectionTree.files) { - selectedFiles = [ - ...selectedFiles, - ...Object.keys(selectionTree.files).reduce((previousValue, filename) => { - const file = selectionTree.files[filename]; - - if (file.isSelected) { - previousValue.push(file.index); - } - - return previousValue; - }, []), - ]; - } - - if (selectionTree.directories) { - Object.keys(selectionTree.directories).forEach(directory => { - selectedFiles = [...selectedFiles, ...this.getSelectedFiles(selectionTree.directories[directory])]; - }); - } - - return selectedFiles; - } - - handleDownloadButtonClick = event => { - event.preventDefault(); - const baseURI = ConfigStore.getBaseURI(); - const link = document.createElement('a'); - link.download = `${this.props.torrent.name}.tar`; - link.href = `${baseURI}api/download?hash=${this.props.torrent.hash}&files=${this.state.selectedFiles.join(',')}`; - link.style.display = 'none'; - document.body.appendChild(link); // Fix for Firefox 58+ - link.click(); - }; - - handleFormChange = ({event}) => { - if (event.target.name === 'file-priority') { - this.handlePriorityChange(); - TorrentActions.setFilePriority(this.props.hash, this.state.selectedFiles, event.target.value); - } - }; - - handleItemSelect(selectedItem) { - this.hasSelectionChanged = true; - this.setState(state => { - const selectedItems = this.mergeSelection(selectedItem, state.selectedItems, 0, this.props.fileTree); - const selectedFiles = this.getSelectedFiles(selectedItems); - - return { - selectedItems, - allSelected: false, - selectedFiles, - }; - }); - } - - handlePriorityChange() { - this.hasPriorityChanged = true; - } - - handleSelectAllClick() { - this.hasSelectionChanged = true; - - this.setState((state, props) => { - const selectedItems = this.selectAll(state.selectedItems, props.fileTree, state.allSelected); - const selectedFiles = this.getSelectedFiles(selectedItems); - - return { - selectedItems, - allSelected: !state.allSelected, - selectedFiles, - }; - }); - } - - isLoaded() { - return this.props.fileTree != null; - } - - mergeSelection(item, tree = {}, depth = 0, fileTree = {}) { - const {path} = item; - const pathSegment = path[depth]; - const selectionSubTree = item.type === 'file' ? 'files' : 'directories'; - - if (!tree[selectionSubTree]) { - tree[selectionSubTree] = {}; - } - - if (!tree[selectionSubTree][pathSegment]) { - tree[selectionSubTree][pathSegment] = {}; - } - - // If we are not at the clicked depth, then recurse over the path segments. - if (depth++ < path.length - 1) { - if (!tree.directories) { - tree.directories = {[pathSegment]: {}}; - } else if (!tree.directories[pathSegment]) { - tree.directories[pathSegment] = {}; - } - - // Deselect all parent directories if the item in question is being - // de-selected. - if (item.isSelected) { - delete tree.isSelected; - } - - tree.directories[pathSegment] = this.mergeSelection( - item, - tree.directories[pathSegment], - depth, - fileTree.directories[pathSegment], - ); - } else if (item.isSelected) { - delete tree.isSelected; - delete tree[selectionSubTree][pathSegment]; - } else { - let value; - - // If a directory was checked, recursively check all its children. - if (item.type === 'directory') { - value = this.selectAll(tree[selectionSubTree][pathSegment], fileTree[selectionSubTree][pathSegment]); - } else { - value = {...item, isSelected: true}; - } - - tree[selectionSubTree][pathSegment] = value; - } - - return tree; - } - - selectAll(selectionTree = {}, fileTree = {}, deselect = false) { - if (fileTree.files) { - fileTree.files.forEach(file => { - if (!selectionTree.files) { - selectionTree.files = {}; - } - - if (!deselect) { - selectionTree.files[file.filename] = {...file, isSelected: true}; - } else { - delete selectionTree.files[file.filename]; - } - }); - } - - if (fileTree.directories) { - Object.keys(fileTree.directories).forEach(directory => { - if (!selectionTree.directories) { - selectionTree.directories = {}; - } - - if (deselect && selectionTree.directories[directory]) { - delete selectionTree.directories[directory].isSelected; - } - - selectionTree.directories[directory] = this.selectAll( - selectionTree.directories[directory], - fileTree.directories[directory], - deselect, - ); - }); - } - - selectionTree.isSelected = !deselect; - return selectionTree; - } - - render() { - const {fileTree, torrent} = this.props; - let directoryHeadingIconContent = null; - let fileDetailContent = null; - - if (this.isLoaded()) { - directoryHeadingIconContent = ( -
    -
    - - - -
    -
    - -
    -
    - ); - fileDetailContent = ( - - ); - } else { - directoryHeadingIconContent = ; - fileDetailContent = ( -
    - -
    - ); - } - - const directoryHeadingClasses = classnames( - 'directory-tree__node', - 'directory-tree__parent-directory torrent-details__section__heading', - { - 'directory-tree__node--selected': this.state.allSelected, - }, - ); - - const directoryHeading = ( -
    -
    - {directoryHeadingIconContent} -
    {torrent.directory}
    -
    -
    - ); - - const wrapperClasses = classnames('inverse directory-tree__wrapper', { - 'directory-tree__wrapper--toolbar-visible': this.state.selectedFiles.length > 0, - }); - - return ( -
    -
    - - - - {this.state.selectedFiles.length} - - ), - }} - /> - - - - -
    -
    - {directoryHeading} - {fileDetailContent} -
    -
    - ); - } -} - -export default injectIntl(TorrentFiles); diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentGeneralInfo.js b/client/src/javascript/components/modals/torrent-details-modal/TorrentGeneralInfo.js deleted file mode 100644 index 0742b1bc5..000000000 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentGeneralInfo.js +++ /dev/null @@ -1,208 +0,0 @@ -import {FormattedMessage, FormattedNumber, injectIntl} from 'react-intl'; -import React from 'react'; - -import Size from '../../general/Size'; - -class TorrentGeneralInfo extends React.Component { - getTags(tags) { - return tags.map(tag => ( - - {tag} - - )); - } - - render() { - const {torrent} = this.props; - - let dateAdded = null; - if (torrent.dateAdded) { - dateAdded = new Date(torrent.dateAdded * 1000); - } - - let creation = null; - if (torrent.creationDate) { - creation = new Date(torrent.creationDate * 1000); - } - - const VALUE_NOT_AVAILABLE = ( - - - - ); - - return ( -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - {dateAdded - ? `${this.props.intl.formatDate(dateAdded, { - year: 'numeric', - month: 'long', - day: '2-digit', - })} ${this.props.intl.formatTime(dateAdded)}` - : VALUE_NOT_AVAILABLE} -
    - - {torrent.basePath}
    - - - {torrent.ignoreScheduler === '1' - ? this.props.intl.formatMessage({ - id: 'torrents.details.general.scheduler.ignored', - defaultMessage: 'Ignored', - }) - : this.props.intl.formatMessage({ - id: 'torrents.details.general.scheduler.obeyed', - defaultMessage: 'Obeyed', - })} -
    - - - {torrent.tags.length ? this.getTags(torrent.tags) : VALUE_NOT_AVAILABLE} -
    - -
    - - - - % -
    - - - , - total: , - }} - /> -
    - - - , - total: , - }} - /> -
    - -
    - - {torrent.comment || VALUE_NOT_AVAILABLE}
    - - - {creation - ? `${this.props.intl.formatDate(creation, { - year: 'numeric', - month: 'long', - day: '2-digit', - })} ${this.props.intl.formatTime(creation)}` - : VALUE_NOT_AVAILABLE} -
    - - {torrent.hash}
    - - - -
    - - - {torrent.isPrivate === '0' - ? this.props.intl.formatMessage({ - id: 'torrents.details.general.type.public', - defaultMessage: 'Public', - }) - : this.props.intl.formatMessage({ - id: 'torrents.details.general.type.private', - defaultMessage: 'Private', - })} -
    - -
    - - - {torrent.message ? torrent.message : VALUE_NOT_AVAILABLE} -
    -
    - ); - } -} - -export default injectIntl(TorrentGeneralInfo); diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentGeneralInfo.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentGeneralInfo.tsx new file mode 100644 index 000000000..db4b1c9fe --- /dev/null +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentGeneralInfo.tsx @@ -0,0 +1,193 @@ +import {FC} from 'react'; +import {FormattedMessage, FormattedNumber, useIntl} from 'react-intl'; +import {observer} from 'mobx-react'; + +import type {TorrentProperties} from '@shared/types/Torrent'; + +import Size from '../../general/Size'; +import TorrentStore from '../../../stores/TorrentStore'; +import UIStore from '../../../stores/UIStore'; + +const getTags = (tags: TorrentProperties['tags']) => + tags.map((tag) => ( + + {tag} + + )); + +const TorrentGeneralInfo: FC = observer(() => { + const intl = useIntl(); + + if (UIStore.activeModal?.id !== 'torrent-details') { + return null; + } + + const torrent = TorrentStore.torrents[UIStore?.activeModal?.hash]; + if (torrent == null) { + return null; + } + + let dateAdded = null; + if (torrent.dateAdded) { + dateAdded = new Date(torrent.dateAdded * 1000); + } + + let creation = null; + if (torrent.dateCreated) { + creation = new Date(torrent.dateCreated * 1000); + } + + const VALUE_NOT_AVAILABLE = ( + + + + ); + + return ( +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + {dateAdded + ? `${intl.formatDate(dateAdded, { + year: 'numeric', + month: 'long', + day: '2-digit', + })} ${intl.formatTime(dateAdded)}` + : VALUE_NOT_AVAILABLE} +
    + + {torrent.directory}
    + + + {torrent.tags.length ? getTags(torrent.tags) : VALUE_NOT_AVAILABLE} +
    + +
    + + + + % +
    + + + , + total: , + }} + /> +
    + + + , + total: , + }} + /> +
    + +
    + + + {creation + ? `${intl.formatDate(creation, { + year: 'numeric', + month: 'long', + day: '2-digit', + })} ${intl.formatTime(creation)}` + : VALUE_NOT_AVAILABLE} +
    + + {torrent.hash}
    + + + +
    + + + {torrent.isPrivate + ? intl.formatMessage({ + id: 'torrents.details.general.type.private', + }) + : intl.formatMessage({ + id: 'torrents.details.general.type.public', + })} +
    + +
    + + + {torrent.message ? torrent.message : VALUE_NOT_AVAILABLE} +
    +
    + ); +}); + +export default TorrentGeneralInfo; diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.js b/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.js deleted file mode 100644 index d6cec8ad9..000000000 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.js +++ /dev/null @@ -1,134 +0,0 @@ -import {FormattedMessage} from 'react-intl'; -import classnames from 'classnames'; -import React from 'react'; -import stringUtil from '@shared/util/stringUtil'; -import torrentStatusMap from '@shared/constants/torrentStatusMap'; - -import ClockIcon from '../../icons/ClockIcon'; -import DownloadThickIcon from '../../icons/DownloadThickIcon'; -import Duration from '../../general/Duration'; -import PriorityMeter from '../../general/filesystem/PriorityMeter'; -import ProgressBar from '../../general/ProgressBar'; -import Ratio from '../../general/Ratio'; -import RatioIcon from '../../icons/RatioIcon'; -import Size from '../../general/Size'; -import StartIcon from '../../icons/StartIcon'; -import StopIcon from '../../icons/StopIcon'; -import TorrentActions from '../../../actions/TorrentActions'; -import {torrentStatusClasses} from '../../../util/torrentStatusClasses'; -import {torrentStatusIcons} from '../../../util/torrentStatusIcons'; -import UploadThickIcon from '../../icons/UploadThickIcon'; - -const METHODS_TO_BIND = ['getCurrentStatus', 'handleStart', 'handleStop']; - -export default class TorrentHeading extends React.Component { - constructor() { - super(); - - this.state = { - optimisticData: {currentStatus: null}, - }; - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - getCurrentStatus(torrentStatus) { - if (torrentStatus.includes(torrentStatusMap.stopped)) { - return 'stop'; - } - return 'start'; - } - - getTorrentActions(torrent) { - const currentStatus = this.state.optimisticData.currentStatus || this.getCurrentStatus(torrent.status); - const statusIcons = { - start: , - stop: , - }; - const torrentActions = ['start', 'stop']; - const torrentActionElements = [ -
  • - -
  • , - ]; - - torrentActions.forEach((torrentAction, index) => { - const capitalizedAction = stringUtil.capitalize(torrentAction); - const classes = classnames('torrent-details__sub-heading__tertiary', 'torrent-details__action', { - 'is-active': torrentAction === currentStatus, - }); - - torrentActionElements.push( - // TODO: Find a better key - // eslint-disable-next-line react/no-array-index-key -
  • - {statusIcons[torrentAction]} - -
  • , - ); - }); - - return torrentActionElements; - } - - handlePriorityChange(hash, level) { - TorrentActions.setPriority(hash, level); - } - - handleStart() { - this.setState({optimisticData: {currentStatus: 'start'}}); - TorrentActions.startTorrents([this.props.torrent.hash]); - } - - handleStop() { - this.setState({optimisticData: {currentStatus: 'stop'}}); - TorrentActions.stopTorrents([this.props.torrent.hash]); - } - - render() { - const {torrent} = this.props; - const torrentClasses = torrentStatusClasses(torrent, 'torrent-details__header'); - const torrentStatusIcon = torrentStatusIcons(torrent.status); - - return ( -
    -

    {torrent.name}

    -
    -
      -
    • - - -  —  - -
    • -
    • - - -  —  - -
    • -
    • - - -
    • -
    • - - -
    • -
    -
      {this.getTorrentActions(torrent)}
    -
    - -
    - ); - } -} diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.tsx new file mode 100644 index 000000000..913ead6d2 --- /dev/null +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.tsx @@ -0,0 +1,125 @@ +import classnames from 'classnames'; +import {FC, useEffect, useState} from 'react'; +import {FormattedMessage, FormattedNumber} from 'react-intl'; +import {observer} from 'mobx-react'; + +import ClockIcon from '../../icons/ClockIcon'; +import DownloadThickIcon from '../../icons/DownloadThickIcon'; +import Duration from '../../general/Duration'; +import PriorityMeter from '../../general/PriorityMeter'; +import ProgressBar from '../../general/ProgressBar'; +import RatioIcon from '../../icons/RatioIcon'; +import Size from '../../general/Size'; +import StartIcon from '../../icons/StartIcon'; +import StopIcon from '../../icons/StopIcon'; +import TorrentActions from '../../../actions/TorrentActions'; +import torrentStatusClasses from '../../../util/torrentStatusClasses'; +import torrentStatusIcons from '../../../util/torrentStatusIcons'; +import TorrentStore from '../../../stores/TorrentStore'; +import UploadThickIcon from '../../icons/UploadThickIcon'; +import UIStore from '../../../stores/UIStore'; + +const TorrentHeading: FC = observer(() => { + const torrent = + UIStore.activeModal?.id === 'torrent-details' ? TorrentStore.torrents[UIStore.activeModal.hash] : undefined; + const [torrentStatus, setTorrentStatus] = useState<'start' | 'stop'>('stop'); + + useEffect(() => { + if (torrent?.status.includes('stopped')) { + setTorrentStatus('stop'); + } else { + setTorrentStatus('start'); + } + }, [torrent?.status]); + + if (torrent == null) { + return null; + } + + const torrentClasses = torrentStatusClasses( + { + status: torrent.status, + upRate: torrent.upRate, + downRate: torrent.downRate, + }, + 'torrent-details__header', + ); + const torrentStatusIcon = torrentStatusIcons(torrent.status); + + return ( +
    +

    {torrent.name}

    +
    +
      +
    • + + +  —  + +
    • +
    • + + +  —  + +
    • +
    • + + +
    • +
    • + + +
    • +
    +
      +
    • + { + TorrentActions.setPriority({ + hashes: [`${hash}`], + priority: level, + }); + }} + /> +
    • +
    • { + setTorrentStatus('start'); + TorrentActions.startTorrents({ + hashes: [torrent.hash], + }); + }}> + + +
    • +
    • { + setTorrentStatus('stop'); + TorrentActions.stopTorrents({ + hashes: [torrent.hash], + }); + }}> + + +
    • +
    +
    + +
    + ); +}); + +export default TorrentHeading; diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentMediainfo.js b/client/src/javascript/components/modals/torrent-details-modal/TorrentMediainfo.js deleted file mode 100644 index 9414887bf..000000000 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentMediainfo.js +++ /dev/null @@ -1,164 +0,0 @@ -import {Button} from 'flood-ui-kit'; -import Clipboard from 'clipboard'; -import {defineMessages, FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import ClipboardIcon from '../../icons/ClipboardIcon'; -import connectStores from '../../../util/connectStores'; -import EventTypes from '../../../constants/EventTypes'; -import FloodActions from '../../../actions/FloodActions'; -import Tooltip from '../../general/Tooltip'; -import TorrentStore from '../../../stores/TorrentStore'; - -const MESSAGES = defineMessages({ - copy: { - id: 'general.clipboard.copy', - defaultMessage: 'Copy', - }, - copied: { - id: 'general.clipboard.copied', - defaultMessage: 'Copied', - }, - execError: { - id: 'mediainfo.execError', - defaultMessage: - 'An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.', - }, - fetching: { - id: 'mediainfo.fetching', - defaultMessage: 'Fetching...', - }, - heading: { - id: 'mediainfo.heading', - defaultMessage: 'Mediainfo Output', - }, -}); - -class TorrentMediainfo extends React.Component { - clipboard = null; - - timeoutId = null; - - state = { - copiedToClipboard: false, - isFetchingMediainfo: true, - fetchMediainfoError: null, - }; - - componentDidMount() { - FloodActions.fetchMediainfo({hash: this.props.hash}).then( - () => { - this.setState({ - isFetchingMediainfo: false, - fetchMediainfoError: null, - }); - }, - error => { - this.setState({ - isFetchingMediainfo: false, - fetchMediainfoError: error, - }); - }, - ); - } - - componentDidUpdate() { - if (this.copyButtonRef && this.clipboard == null) { - this.clipboard = new Clipboard(this.copyButtonRef, { - text: () => this.props.mediainfo, - }); - - this.clipboard.on('success', this.handleCopySuccess); - } - } - - componentWillUnmount() { - if (this.timeoutId != null) { - global.clearTimeout(this.timeoutId); - } - } - - handleCopyButtonMouseLeave = () => { - this.timeoutId = global.setTimeout(() => { - this.setState({ - copiedToClipboard: false, - }); - }, 500); - }; - - handleCopySuccess = () => { - this.setState({ - copiedToClipboard: true, - }); - }; - - render() { - if (this.state.isFetchingMediainfo) { - return ( -
    - -
    - ); - } - - if (this.state.fetchMediainfoError) { - const errorData = this.state.fetchMediainfoError.data || {}; - - return ( -
    -

    - -

    -
    {JSON.stringify(errorData.error, null, 2)}
    -
    - ); - } - - let tooltipText = this.props.intl.formatMessage(MESSAGES.copy); - - if (this.state.copiedToClipboard) { - tooltipText = this.props.intl.formatMessage(MESSAGES.copied); - } - - return ( -
    -
    -
    - - - -
    - - - -
    -
    {this.props.mediainfo}
    -
    - ); - } -} - -const ConnectedTorrentMediainfo = connectStores(injectIntl(TorrentMediainfo), () => { - return [ - { - store: TorrentStore, - event: EventTypes.FLOOD_FETCH_MEDIAINFO_SUCCESS, - getValue: ({store, props}) => { - return { - mediainfo: store.getMediainfo(props.hash), - }; - }, - }, - ]; -}); - -export default ConnectedTorrentMediainfo; diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentMediainfo.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentMediainfo.tsx new file mode 100644 index 000000000..e9b04d5bc --- /dev/null +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentMediainfo.tsx @@ -0,0 +1,153 @@ +import axios from 'axios'; +import Clipboard from 'clipboard'; +import {Component} from 'react'; +import {defineMessages, FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; + +import {Button} from '../../../ui'; +import ClipboardIcon from '../../icons/ClipboardIcon'; +import Tooltip from '../../general/Tooltip'; +import TorrentActions from '../../../actions/TorrentActions'; +import UIStore from '../../../stores/UIStore'; + +const MESSAGES = defineMessages({ + copy: { + id: 'general.clipboard.copy', + }, + copied: { + id: 'general.clipboard.copied', + }, + execError: { + id: 'mediainfo.execError', + }, + fetching: { + id: 'mediainfo.fetching', + }, + heading: { + id: 'mediainfo.heading', + }, +}); + +interface TorrentMediainfoStates { + copiedToClipboard: boolean; +} + +class TorrentMediainfo extends Component { + mediainfo: string | null = null; + isFetchingMediainfo = true; + fetchMediainfoError: Error | null = null; + + cancelToken = axios.CancelToken.source(); + clipboard: Clipboard | null = null; + copyButtonRef: HTMLButtonElement | null = null; + timeoutId: NodeJS.Timeout | null = null; + + constructor(props: WrappedComponentProps) { + super(props); + + this.state = { + copiedToClipboard: false, + }; + + if (UIStore.activeModal?.id === 'torrent-details') { + TorrentActions.fetchMediainfo(UIStore.activeModal?.hash, this.cancelToken.token).then( + (mediainfo) => { + this.fetchMediainfoError = null; + this.mediainfo = mediainfo.output; + this.isFetchingMediainfo = false; + this.forceUpdate(); + }, + (error) => { + if (!axios.isCancel(error)) { + this.fetchMediainfoError = error.response.data; + this.isFetchingMediainfo = false; + this.forceUpdate(); + } + }, + ); + } + } + + componentDidUpdate() { + if (this.mediainfo === null) { + return; + } + + if (this.copyButtonRef && this.clipboard == null) { + this.clipboard = new Clipboard(this.copyButtonRef, { + text: () => this.mediainfo as string, + }); + + this.clipboard.on('success', this.handleCopySuccess); + } + } + + componentWillUnmount() { + this.cancelToken.cancel(); + if (this.timeoutId != null) { + global.clearTimeout(this.timeoutId); + } + } + + handleCopyButtonMouseLeave = () => { + this.timeoutId = global.setTimeout(() => { + this.setState({ + copiedToClipboard: false, + }); + }, 500); + }; + + handleCopySuccess = () => { + this.setState({ + copiedToClipboard: true, + }); + }; + + render() { + const {intl} = this.props; + + let headingMessage = MESSAGES.heading; + if (this.isFetchingMediainfo) { + headingMessage = MESSAGES.fetching; + } else if (this.fetchMediainfoError) { + headingMessage = MESSAGES.execError; + } + + let tooltipMessage = MESSAGES.copy; + if (this.state.copiedToClipboard) { + tooltipMessage = MESSAGES.copied; + } + + return ( +
    +
    +
    + + + +
    + {this.isFetchingMediainfo || this.fetchMediainfoError ? null : ( + + + + )} +
    + {this.fetchMediainfoError ? ( +
    {this.fetchMediainfoError.message}
    + ) : ( +
    {this.mediainfo}
    + )} +
    + ); + } +} + +export default injectIntl(TorrentMediainfo); diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentPeers.js b/client/src/javascript/components/modals/torrent-details-modal/TorrentPeers.js deleted file mode 100644 index 793ee356c..000000000 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentPeers.js +++ /dev/null @@ -1,122 +0,0 @@ -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -import Badge from '../../general/Badge'; -import Size from '../../general/Size'; -import Checkmark from '../../icons/Checkmark'; - -const checkmark = ; - -export default class TorrentPeers extends React.Component { - constructor() { - super(); - - this.state = { - erroredCountryImages: [], - }; - } - - flagImageAsErrored(countryCode) { - const {erroredCountryImages} = this.state; - erroredCountryImages.push(countryCode); - this.setState({erroredCountryImages}); - } - - getImageErrorHandlerFn(countryCode) { - return () => this.flagImageAsErrored(countryCode); - } - - render() { - const {peers} = this.props; - - if (peers) { - const {erroredCountryImages} = this.state; - const peerList = peers.map(peer => { - const {country: countryCode} = peer; - const encryptedIcon = peer.isEncrypted ? checkmark : null; - let peerCountry = null; - - if (countryCode) { - let image = null; - - if (!erroredCountryImages.includes(countryCode)) { - let flagImageSrc; - try { - // We can ignore the lint warnings becuase we need all of the flags available for request. - // eslint-disable-next-line global-require,no-undef,import/no-dynamic-require - flagImageSrc = require(`../../../../images/flags/${countryCode.toLowerCase()}.png`); - } catch (err) { - this.flagImageAsErrored(countryCode); - flagImageSrc = null; - } - - if (flagImageSrc) { - image = ( - {countryCode} - ); - } - } - - peerCountry = ( - - {image} - {countryCode} - - ); - } - - return ( - - - {peerCountry} - {peer.address} - - - - - - - - {peer.completedPercent}% - {peer.clientVersion} - {encryptedIcon} - - ); - }); - - return ( -
    - - - - - - - - - - - - {peerList} -
    - - {peers.length} - DLUL%ClientEnc
    -
    - ); - } - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentPeers.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentPeers.tsx new file mode 100644 index 000000000..2cb26e2ef --- /dev/null +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentPeers.tsx @@ -0,0 +1,89 @@ +import {FC, Suspense, useEffect, useState} from 'react'; +import {FormattedMessage} from 'react-intl'; +import {useInterval} from 'react-use'; + +import type {TorrentPeer} from '@shared/types/TorrentPeer'; + +import Badge from '../../general/Badge'; +import ConfigStore from '../../../stores/ConfigStore'; +import CountryFlagIcon from '../../icons/CountryFlagIcon'; +import Size from '../../general/Size'; +import Checkmark from '../../icons/Checkmark'; +import LockIcon from '../../icons/LockIcon'; +import SpinnerIcon from '../../icons/SpinnerIcon'; +import TorrentActions from '../../../actions/TorrentActions'; +import UIStore from '../../../stores/UIStore'; + +const TorrentPeers: FC = () => { + const [peers, setPeers] = useState>([]); + const [pollingDelay, setPollingDelay] = useState(null); + + const fetchPeers = () => { + setPollingDelay(null); + if (UIStore.activeModal?.id === 'torrent-details') { + TorrentActions.fetchTorrentPeers(UIStore.activeModal?.hash).then((data) => { + if (data != null) { + setPeers(data); + } + }); + } + setPollingDelay(ConfigStore.pollInterval); + }; + + useEffect(() => fetchPeers(), []); + useInterval(() => fetchPeers(), pollingDelay); + + return ( +
    + + + + + + + + + + + + + + {peers.map((peer) => { + const {country: countryCode} = peer; + const encryptedIcon = peer.isEncrypted ? : null; + const incomingIcon = peer.isIncoming ? : null; + + return ( + + + + + + + + + + ); + })} + +
    + + {peers.length} + DLUL%ClientEncIn
    + + }> + + + {countryCode} + + {peer.address} + + + + + {`${Math.ceil(peer.completedPercent)}%`}{peer.clientVersion}{encryptedIcon}{incomingIcon}
    +
    + ); +}; + +export default TorrentPeers; diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentTrackers.js b/client/src/javascript/components/modals/torrent-details-modal/TorrentTrackers.js deleted file mode 100644 index db6c7b70a..000000000 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentTrackers.js +++ /dev/null @@ -1,49 +0,0 @@ -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -import Badge from '../../general/Badge'; - -export default class TorrentTrackrs extends React.Component { - render() { - const trackers = this.props.trackers || []; - - const trackerCount = trackers.length; - const trackerTypes = ['http', 'udp', 'dht']; - - const trackerDetails = trackers.map(tracker => ( - - {tracker.url} - {trackerTypes[tracker.type - 1]} - - )); - - if (trackerCount) { - return ( -
    - - - - - - - - {trackerDetails} -
    - - {trackerCount} - - -
    -
    - ); - } - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentTrackers.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentTrackers.tsx new file mode 100644 index 000000000..fd60bca32 --- /dev/null +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentTrackers.tsx @@ -0,0 +1,53 @@ +import {FC, useEffect, useState} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import type {TorrentTracker} from '@shared/types/TorrentTracker'; + +import Badge from '../../general/Badge'; +import TorrentActions from '../../../actions/TorrentActions'; +import UIStore from '../../../stores/UIStore'; + +const TorrentTrackers: FC = () => { + const [trackers, setTrackers] = useState>([]); + + const trackerCount = trackers.length; + const trackerTypes = ['http', 'udp', 'dht']; + + const trackerDetails = trackers.map((tracker) => ( + + {tracker.url} + {trackerTypes[tracker.type - 1]} + + )); + + useEffect(() => { + if (UIStore.activeModal?.id === 'torrent-details') { + TorrentActions.fetchTorrentTrackers(UIStore.activeModal?.hash).then((data) => { + if (data != null) { + setTrackers(data); + } + }); + } + }, []); + + return ( +
    + + + + + + + + {trackerDetails} +
    + + {trackerCount} + + +
    +
    + ); +}; + +export default TorrentTrackers; diff --git a/client/src/javascript/components/sidebar/DiskUsage.js b/client/src/javascript/components/sidebar/DiskUsage.js deleted file mode 100644 index c490941b7..000000000 --- a/client/src/javascript/components/sidebar/DiskUsage.js +++ /dev/null @@ -1,99 +0,0 @@ -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -import EventTypes from '../../constants/EventTypes'; -import DiskUsageStore from '../../stores/DiskUsageStore'; -import Size from '../general/Size'; -import Tooltip from '../general/Tooltip'; -import connectStores from '../../util/connectStores'; -import ProgressBar from '../general/ProgressBar'; -import SettingsStore from '../../stores/SettingsStore'; - -const DiskUsageTooltipItem = ({label, value}) => { - return ( -
  • - - -
  • - ); -}; - -class DiskUsage extends React.Component { - getDisks() { - const {disks, mountPoints} = this.props; - const diskMap = disks.reduce((disksByTarget, disk) => { - disksByTarget[disk.target] = disk; - return disksByTarget; - }, {}); - return mountPoints - .filter(target => target in diskMap) - .map(target => diskMap[target]) - .map(d => { - return ( -
  • - - } - /> - } - /> - } - /> - - } - position="top" - wrapperClassName="diskusage__item"> -
    - {d.target} - {Math.round((100 * d.used) / d.size)}% -
    - -
    -
  • - ); - }); - } - - render() { - const disks = this.getDisks(); - - if (disks.length === 0) { - return null; - } - - return ( -
      -
    • - -
    • - {disks} -
    - ); - } -} - -export default connectStores(DiskUsage, () => [ - { - store: DiskUsageStore, - event: EventTypes.DISK_USAGE_CHANGE, - getValue: ({store}) => ({ - disks: store.getDiskUsage(), - }), - }, - { - store: SettingsStore, - event: EventTypes.SETTINGS_CHANGE, - getValue: ({store}) => { - return { - mountPoints: store.getFloodSettings('mountPoints'), - }; - }, - }, -]); diff --git a/client/src/javascript/components/sidebar/DiskUsage.tsx b/client/src/javascript/components/sidebar/DiskUsage.tsx new file mode 100644 index 000000000..61f9a1d31 --- /dev/null +++ b/client/src/javascript/components/sidebar/DiskUsage.tsx @@ -0,0 +1,79 @@ +import {FC, ReactNode, ReactNodeArray} from 'react'; +import {FormattedMessage} from 'react-intl'; +import {observer} from 'mobx-react'; + +import type {Disk} from '@shared/types/DiskUsage'; + +import DiskUsageStore from '../../stores/DiskUsageStore'; +import Size from '../general/Size'; +import Tooltip from '../general/Tooltip'; +import ProgressBar from '../general/ProgressBar'; +import SettingStore from '../../stores/SettingStore'; + +interface DiskUsageTooltipItemProps { + label: ReactNode; + value: number; +} + +const DiskUsageTooltipItem: FC = ({label, value}: DiskUsageTooltipItemProps) => ( +
  • + + +
  • +); + +const DiskUsage: FC = observer(() => { + const {disks} = DiskUsageStore; + const {mountPoints} = SettingStore.floodSettings; + + if (disks == null || mountPoints == null) { + return null; + } + + const diskMap = disks.reduce( + (disksByTarget: Record, disk: Disk) => ({ + ...disksByTarget, + [disk.target]: disk, + }), + {}, + ); + + const diskNodes: ReactNodeArray = mountPoints + .filter((target) => target in diskMap) + .map((target) => diskMap[target]) + .map((d) => ( +
  • + + } /> + } /> + } /> + + } + position="top" + wrapperClassName="diskusage__item"> +
    + {d.target} + {Math.round((100 * d.used) / d.size)}% +
    + +
    +
  • + )); + + if (diskNodes == null || diskNodes.length === 0) { + return null; + } + + return ( +
      +
    • + +
    • + {diskNodes} +
    + ); +}); + +export default DiskUsage; diff --git a/client/src/javascript/components/sidebar/FeedsButton.js b/client/src/javascript/components/sidebar/FeedsButton.js deleted file mode 100644 index 67fcb74b7..000000000 --- a/client/src/javascript/components/sidebar/FeedsButton.js +++ /dev/null @@ -1,55 +0,0 @@ -import {defineMessages, injectIntl} from 'react-intl'; -import React from 'react'; - -import FeedIcon from '../icons/FeedIcon'; -import Tooltip from '../general/Tooltip'; -import UIActions from '../../actions/UIActions'; - -const MESSAGES = defineMessages({ - feeds: { - id: 'sidebar.button.feeds', - defaultMessage: 'Feeds', - }, -}); - -const METHODS_TO_BIND = ['handleFeedsButtonClick']; - -class FeedsButton extends React.Component { - constructor() { - super(); - - this.tooltipRef = null; - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - handleFeedsButtonClick() { - if (this.tooltipRef != null) { - this.tooltipRef.dismissTooltip(); - } - - UIActions.displayModal({id: 'feeds'}); - } - - render() { - const label = this.props.intl.formatMessage(MESSAGES.feeds); - - return ( - { - this.tooltipRef = ref; - }} - position="bottom" - wrapperClassName="sidebar__action sidebar__icon-button - sidebar__icon-button--interactive tooltip__wrapper"> - - - ); - } -} - -export default injectIntl(FeedsButton); diff --git a/client/src/javascript/components/sidebar/FeedsButton.tsx b/client/src/javascript/components/sidebar/FeedsButton.tsx new file mode 100644 index 000000000..365983e5e --- /dev/null +++ b/client/src/javascript/components/sidebar/FeedsButton.tsx @@ -0,0 +1,38 @@ +import {defineMessages, useIntl} from 'react-intl'; +import {FC, useRef} from 'react'; + +import FeedIcon from '../icons/FeedIcon'; +import Tooltip from '../general/Tooltip'; +import UIActions from '../../actions/UIActions'; + +const MESSAGES = defineMessages({ + feeds: { + id: 'sidebar.button.feeds', + }, +}); + +const FeedsButton: FC = () => { + const intl = useIntl(); + const label = intl.formatMessage(MESSAGES.feeds); + const tooltipRef = useRef(null); + + return ( + { + if (tooltipRef.current != null) { + tooltipRef.current.dismissTooltip(); + } + + UIActions.displayModal({id: 'feeds'}); + }} + ref={tooltipRef} + position="bottom" + wrapperClassName="sidebar__action sidebar__icon-button + sidebar__icon-button--interactive tooltip__wrapper"> + + + ); +}; + +export default FeedsButton; diff --git a/client/src/javascript/components/sidebar/LogoutButton.js b/client/src/javascript/components/sidebar/LogoutButton.js deleted file mode 100644 index 1363c5d96..000000000 --- a/client/src/javascript/components/sidebar/LogoutButton.js +++ /dev/null @@ -1,37 +0,0 @@ -import {defineMessages, injectIntl} from 'react-intl'; -import React from 'react'; - -import AuthActions from '../../actions/AuthActions'; -import Logout from '../icons/Logout'; -import Tooltip from '../general/Tooltip'; - -const MESSAGES = defineMessages({ - logOut: { - id: 'sidebar.button.log.out', - defaultMessage: 'Log Out', - }, -}); - -class LogoutButton extends React.Component { - handleLogoutClick() { - AuthActions.logout().then(() => { - window.location.reload(); - }); - } - - render() { - return ( - - - - ); - } -} - -export default injectIntl(LogoutButton); diff --git a/client/src/javascript/components/sidebar/LogoutButton.tsx b/client/src/javascript/components/sidebar/LogoutButton.tsx new file mode 100644 index 000000000..7e11b46a9 --- /dev/null +++ b/client/src/javascript/components/sidebar/LogoutButton.tsx @@ -0,0 +1,35 @@ +import {FC} from 'react'; +import {useIntl} from 'react-intl'; + +import AuthActions from '../../actions/AuthActions'; +import ConfigStore from '../../stores/ConfigStore'; +import Logout from '../icons/Logout'; +import Tooltip from '../general/Tooltip'; + +const LogoutButton: FC = () => { + const intl = useIntl(); + + if (ConfigStore.authMethod === 'none') { + return null; + } + + return ( + + AuthActions.logout().then(() => { + window.location.reload(); + }) + } + position="bottom" + wrapperClassName="sidebar__action sidebar__action--last + sidebar__icon-button sidebar__icon-button--interactive + tooltip__wrapper"> + + + ); +}; + +export default LogoutButton; diff --git a/client/src/javascript/components/sidebar/NotificationsButton.js b/client/src/javascript/components/sidebar/NotificationsButton.js deleted file mode 100644 index 1220ed18f..000000000 --- a/client/src/javascript/components/sidebar/NotificationsButton.js +++ /dev/null @@ -1,360 +0,0 @@ -import classnames from 'classnames'; -import {defineMessages, FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import FloodActions from '../../actions/FloodActions'; -import ChevronLeftIcon from '../icons/ChevronLeftIcon'; -import ChevronRightIcon from '../icons/ChevronRightIcon'; -import connectStores from '../../util/connectStores'; -import CustomScrollbars from '../general/CustomScrollbars'; -import EventTypes from '../../constants/EventTypes'; -import LoadingIndicatorDots from '../icons/LoadingIndicatorDots'; -import NotificationIcon from '../icons/NotificationIcon'; -import NotificationStore from '../../stores/NotificationStore'; -import Tooltip from '../general/Tooltip'; - -const loadingIndicatorIcon = ; - -const MESSAGES = defineMessages({ - notifications: { - id: 'sidebar.button.notifications', - defaultMessage: 'Notifications', - }, - 'notification.torrent.finished.heading': { - id: 'notification.torrent.finished.heading', - defaultMessage: 'Finished Downloading', - }, - 'notification.torrent.finished.body': { - id: 'notification.torrent.finished.body', - defaultMessage: '{name}', - }, - 'notification.torrent.errored.heading': { - id: 'notification.torrent.errored.heading', - defaultMessage: 'Error Reported', - }, - 'notification.torrent.errored.body': { - id: 'notification.torrent.errored.body', - defaultMessage: '{name}', - }, - 'notification.feed.downloaded.torrent.heading': { - id: 'notification.feed.downloaded.torrent.heading', - defaultMessage: 'Matched Feed Rule', - }, - clearAll: { - id: 'notification.clear.all', - defaultMessage: 'Clear All', - }, - showing: { - id: 'notification.showing', - defaultMessage: 'Showing', - }, - at: { - id: 'general.at', - defaultMessage: 'at', - }, - to: { - id: 'general.to', - defaultMessage: 'to', - }, - of: { - id: 'general.of', - defaultMessage: 'of', - }, -}); - -const NOTIFICATIONS_PER_PAGE = 10; - -class NotificationsButton extends React.Component { - tooltipRef = null; - - state = { - isLoading: false, - paginationStart: 0, - }; - - componentDidMount() { - NotificationStore.listen(EventTypes.NOTIFICATIONS_COUNT_CHANGE, this.handleNotificationCountChange); - } - - componentWillUnmount() { - NotificationStore.unlisten(EventTypes.NOTIFICATIONS_COUNT_CHANGE, this.handleNotificationCountChange); - } - - fetchNotifications = () => { - this.setState({isLoading: true}); - - FloodActions.fetchNotifications({ - id: 'notification-tooltip', - limit: NOTIFICATIONS_PER_PAGE, - start: this.state.paginationStart, - }).then(() => { - this.setState({isLoading: false}); - }); - }; - - getBadge() { - if (this.props.count.total > 0) { - return {this.props.count.total}; - } - - return null; - } - - getBottomToolbar = () => { - if (this.props.count.total > 0) { - const newerButtonClass = classnames( - 'toolbar__item toolbar__item--button', - 'tooltip__content--padding-surrogate', - { - 'is-disabled': this.state.paginationStart === 0, - }, - ); - const olderButtonClass = classnames( - 'toolbar__item toolbar__item--button', - 'tooltip__content--padding-surrogate', - { - 'is-disabled': this.state.paginationStart + NOTIFICATIONS_PER_PAGE >= this.props.count.total, - }, - ); - - const olderFrom = this.state.paginationStart + NOTIFICATIONS_PER_PAGE + 1; - let olderTo = this.state.paginationStart + NOTIFICATIONS_PER_PAGE * 2; - let newerFrom = this.state.paginationStart - NOTIFICATIONS_PER_PAGE; - const newerTo = this.state.paginationStart; - - if (olderTo > this.props.count.total) { - olderTo = this.props.count.total; - } - - if (newerFrom < 0) { - newerFrom = 0; - } - - return ( -
      -
    • - {newerFrom + 1} – {newerTo} -
    • -
    • - {this.props.intl.formatMessage(MESSAGES.clearAll)} -
    • -
    • - {olderFrom} – - {olderTo} -
    • -
    - ); - } - - return null; - }; - - getNotification = (notification, index) => { - const date = this.props.intl.formatDate(notification.ts, {year: 'numeric', month: 'long', day: '2-digit'}); - const time = this.props.intl.formatTime(notification.ts); - - let notificationBody = null; - - if (notification.id === 'notification.feed.downloaded.torrent') { - notificationBody = ( - - {notification.data.ruleLabel} - {' / '} - {notification.data.feedLabel} - - ), - title: notification.data.title, - }} - /> - ); - } else { - notificationBody = this.props.intl.formatMessage(MESSAGES[`${notification.id}.body`], notification.data); - } - - return ( -
  • -
    - - {this.props.intl.formatMessage(MESSAGES[`${notification.id}.heading`])} - - ' — ' - - {date} {this.props.intl.formatMessage(MESSAGES.at)} {time} - -
    -
    {notificationBody}
    -
  • - ); - }; - - getTopToolbar() { - if (this.props.count.total > NOTIFICATIONS_PER_PAGE) { - let countStart = this.state.paginationStart + 1; - let countEnd = this.state.paginationStart + NOTIFICATIONS_PER_PAGE; - - if (countStart > this.props.count.total) { - countStart = this.props.count.total; - } - - if (countEnd > this.props.count.total) { - countEnd = this.props.count.total; - } - - return ( -
    - - {`${this.props.intl.formatMessage(MESSAGES.showing)} `} - - {countStart} - {` ${this.props.intl.formatMessage(MESSAGES.to)} `} - {countEnd} - - {` ${this.props.intl.formatMessage(MESSAGES.of)} `} - {this.props.count.total} - -
    - ); - } - - return null; - } - - getTooltipContent = () => { - if (this.props.count.total === 0) { - return ( -
    - {this.props.intl.formatMessage(MESSAGES.notifications)} -
    - ); - } - - const {isLoading} = this.state; - const {notifications = []} = this.props; - - const notificationsWrapperClasses = classnames('notifications', { - 'notifications--is-loading': isLoading, - }); - - return ( -
    - {this.getTopToolbar()} -
    {loadingIndicatorIcon}
    - -
      - {notifications.map(this.getNotification)} -
    -
    - {this.getBottomToolbar()} -
    - ); - }; - - handleClearNotificationsClick = () => { - this.setState({ - paginationStart: 0, - }); - - NotificationStore.clearAll({ - id: 'notification-tooltip', - limit: NOTIFICATIONS_PER_PAGE, - }); - - if (this.tooltipRef != null) { - this.tooltipRef.dismissTooltip(); - } - }; - - handleNotificationCountChange = () => { - if (this.tooltipRef != null && this.tooltipRef.isOpen()) { - this.fetchNotifications(); - } - }; - - handleNewerNotificationsClick = () => { - if (this.state.paginationStart - NOTIFICATIONS_PER_PAGE >= 0) { - this.setState(state => { - const paginationStart = state.paginationStart - NOTIFICATIONS_PER_PAGE; - return { - paginationStart, - }; - }, this.fetchNotifications); - } - }; - - handleOlderNotificationsClick = () => { - if (this.props.count.total > this.state.paginationStart + NOTIFICATIONS_PER_PAGE) { - this.setState(state => { - const paginationStart = state.paginationStart + NOTIFICATIONS_PER_PAGE; - return { - paginationStart, - }; - }, this.fetchNotifications); - } - }; - - handleTooltipOpen = () => { - this.fetchNotifications(); - }; - - render() { - return ( - { - this.tooltipRef = ref; - }} - width={this.props.count.total === 0 ? null : 340} - position="bottom" - wrapperClassName="sidebar__action sidebar__icon-button - tooltip__wrapper"> - - {this.getBadge()} - - ); - } -} - -const ConnectedNotificationsButton = connectStores(injectIntl(NotificationsButton), () => { - return [ - { - store: NotificationStore, - event: EventTypes.NOTIFICATIONS_FETCH_SUCCESS, - getValue: ({store}) => { - const tooltipNotificationState = store.getNotifications('notification-tooltip'); - - return { - count: tooltipNotificationState.count, - limit: tooltipNotificationState.limit, - notifications: tooltipNotificationState.notifications, - start: tooltipNotificationState.start, - }; - }, - }, - { - store: NotificationStore, - event: EventTypes.NOTIFICATIONS_COUNT_CHANGE, - getValue: ({store}) => { - return { - count: store.getNotificationCount(), - }; - }, - }, - ]; -}); - -export default ConnectedNotificationsButton; diff --git a/client/src/javascript/components/sidebar/NotificationsButton.tsx b/client/src/javascript/components/sidebar/NotificationsButton.tsx new file mode 100644 index 000000000..7823d8090 --- /dev/null +++ b/client/src/javascript/components/sidebar/NotificationsButton.tsx @@ -0,0 +1,311 @@ +import classnames from 'classnames'; +import {Component, createRef} from 'react'; +import {defineMessages, injectIntl, WrappedComponentProps} from 'react-intl'; +import {observer} from 'mobx-react'; +import {reaction} from 'mobx'; + +import type {Notification} from '@shared/types/Notification'; + +import FloodActions from '../../actions/FloodActions'; +import ChevronLeftIcon from '../icons/ChevronLeftIcon'; +import ChevronRightIcon from '../icons/ChevronRightIcon'; +import LoadingIndicatorDots from '../icons/LoadingIndicatorDots'; +import NotificationIcon from '../icons/NotificationIcon'; +import NotificationStore from '../../stores/NotificationStore'; +import Tooltip from '../general/Tooltip'; + +interface NotificationsButtonStates { + isLoading: boolean; + paginationStart: number; + prevHeight: number; +} + +const loadingIndicatorIcon = ; + +const MESSAGES = defineMessages({ + 'notification.torrent.finished.heading': { + id: 'notification.torrent.finished.heading', + }, + 'notification.torrent.finished.body': { + id: 'notification.torrent.finished.body', + }, + 'notification.torrent.errored.heading': { + id: 'notification.torrent.errored.heading', + }, + 'notification.torrent.errored.body': { + id: 'notification.torrent.errored.body', + }, + 'notification.feed.torrent.added.heading': { + id: 'notification.feed.torrent.added.heading', + }, + 'notification.feed.torrent.added.body': { + id: 'notification.feed.torrent.added.body', + }, + noNotification: { + id: 'notification.no.notification', + }, + clearAll: { + id: 'notification.clear.all', + }, + showing: { + id: 'notification.showing', + }, + at: { + id: 'general.at', + }, + to: { + id: 'general.to', + }, + of: { + id: 'general.of', + }, +}); + +const NOTIFICATIONS_PER_PAGE = 10; + +@observer +class NotificationsButton extends Component { + tooltipRef: Tooltip | null = null; + notificationsListRef = createRef(); + + constructor(props: WrappedComponentProps) { + super(props); + + reaction( + () => NotificationStore.notificationCount, + (count) => { + if (count.total > 0 && this.tooltipRef?.isOpen()) this.fetchNotifications(); + }, + ); + + this.state = { + isLoading: false, + paginationStart: 0, + prevHeight: 0, + }; + } + + getBottomToolbar = () => { + const {notificationCount} = NotificationStore; + + if (notificationCount.total > 0) { + const newerButtonClass = classnames( + 'toolbar__item toolbar__item--button', + 'tooltip__content--padding-surrogate', + { + 'is-disabled': this.state.paginationStart === 0, + }, + ); + const olderButtonClass = classnames( + 'toolbar__item toolbar__item--button', + 'tooltip__content--padding-surrogate', + { + 'is-disabled': this.state.paginationStart + NOTIFICATIONS_PER_PAGE >= notificationCount.total, + }, + ); + + const olderFrom = this.state.paginationStart + NOTIFICATIONS_PER_PAGE + 1; + let olderTo = this.state.paginationStart + NOTIFICATIONS_PER_PAGE * 2; + let newerFrom = this.state.paginationStart - NOTIFICATIONS_PER_PAGE; + const newerTo = this.state.paginationStart; + + if (olderTo > notificationCount.total) { + olderTo = notificationCount.total; + } + + if (newerFrom < 0) { + newerFrom = 0; + } + + return ( +
      +
    • + + {`${newerFrom + 1} - ${newerTo}`} +
    • +
    • + {this.props.intl.formatMessage(MESSAGES.clearAll)} +
    • +
    • + {`${olderFrom} - ${olderTo}`} + +
    • +
    + ); + } + + return null; + }; + + getNotification = (notification: Notification, index: number) => { + const {intl} = this.props; + const date = intl.formatDate(notification.ts, { + year: 'numeric', + month: 'long', + day: '2-digit', + }); + const time = intl.formatTime(notification.ts); + + return ( +
  • +
    + + {intl.formatMessage( + MESSAGES[`${notification.id}.heading` as keyof typeof MESSAGES] || {id: 'general.error.unknown'}, + )} + + {' — '} + {`${date} ${intl.formatMessage(MESSAGES.at)} ${time}`} +
    +
    + {intl.formatMessage( + MESSAGES[`${notification.id}.body` as keyof typeof MESSAGES] || { + id: 'general.error.unknown', + }, + notification.data, + )} +
    +
  • + ); + }; + + getTopToolbar() { + const {intl} = this.props; + const {paginationStart} = this.state; + + const {notificationCount} = NotificationStore; + + if (notificationCount.total > NOTIFICATIONS_PER_PAGE) { + let countStart = paginationStart + 1; + let countEnd = paginationStart + NOTIFICATIONS_PER_PAGE; + + if (countStart > notificationCount.total) { + countStart = notificationCount.total; + } + + if (countEnd > notificationCount.total) { + countEnd = notificationCount.total; + } + + return ( +
    + + {`${intl.formatMessage(MESSAGES.showing)} `} + + {countStart} + {` ${intl.formatMessage(MESSAGES.to)} `} + {countEnd} + + {` ${intl.formatMessage(MESSAGES.of)} `} + {notificationCount.total} + +
    + ); + } + + return null; + } + + fetchNotifications = () => { + this.setState({isLoading: true}); + + FloodActions.fetchNotifications({ + id: 'notification-tooltip', + limit: NOTIFICATIONS_PER_PAGE, + start: this.state.paginationStart, + }).then(() => { + this.setState({isLoading: false}); + }); + }; + + handleClearNotificationsClick = () => { + this.setState({ + paginationStart: 0, + prevHeight: 0, + }); + + if (this.tooltipRef != null) { + this.tooltipRef.dismissTooltip(); + } + + FloodActions.clearNotifications(); + }; + + handleNewerNotificationsClick = () => { + if (this.state.paginationStart - NOTIFICATIONS_PER_PAGE >= 0) { + this.setState((state) => { + const paginationStart = state.paginationStart - NOTIFICATIONS_PER_PAGE; + return { + paginationStart, + prevHeight: this.notificationsListRef.current?.clientHeight || 0, + }; + }, this.fetchNotifications); + } + }; + + handleOlderNotificationsClick = () => { + const {notificationCount} = NotificationStore; + + if (notificationCount.total > this.state.paginationStart + NOTIFICATIONS_PER_PAGE) { + this.setState((state) => { + const paginationStart = state.paginationStart + NOTIFICATIONS_PER_PAGE; + return { + paginationStart, + prevHeight: this.notificationsListRef.current?.clientHeight || 0, + }; + }, this.fetchNotifications); + } + }; + + render() { + const {intl} = this.props; + const {isLoading, prevHeight} = this.state; + const {hasNotification, notifications, notificationCount} = NotificationStore; + + return ( + + {this.getTopToolbar()} +
    {loadingIndicatorIcon}
    +
      + {notifications.map(this.getNotification)} +
    + {this.getBottomToolbar()} +
    + ) : ( +
    + {intl.formatMessage(MESSAGES.noNotification)} +
    + ) + } + interactive + onOpen={() => this.fetchNotifications()} + ref={(ref) => { + this.tooltipRef = ref; + }} + width={340} + position="bottom" + wrapperClassName="sidebar__action sidebar__icon-button + tooltip__wrapper"> + + {hasNotification ? {notificationCount.total} : null} + + ); + } +} + +export default injectIntl(NotificationsButton); diff --git a/client/src/javascript/components/sidebar/SearchBox.tsx b/client/src/javascript/components/sidebar/SearchBox.tsx new file mode 100644 index 000000000..d9de47e78 --- /dev/null +++ b/client/src/javascript/components/sidebar/SearchBox.tsx @@ -0,0 +1,89 @@ +import {injectIntl, WrappedComponentProps} from 'react-intl'; +import classnames from 'classnames'; +import {reaction} from 'mobx'; +import * as React from 'react'; + +import Close from '../icons/Close'; +import Search from '../icons/Search'; +import TorrentFilterStore from '../../stores/TorrentFilterStore'; +import UIActions from '../../actions/UIActions'; + +interface SearchBoxStates { + inputFieldKey: number; + isSearchActive: boolean; +} + +class SearchBox extends React.Component { + constructor(props: WrappedComponentProps) { + super(props); + + reaction( + () => TorrentFilterStore.filters.searchFilter, + (searchFilter) => { + if (searchFilter === '') { + this.resetSearch(); + } + }, + ); + + this.state = { + inputFieldKey: 0, + isSearchActive: false, + }; + } + + handleSearchChange = (event: React.ChangeEvent) => { + const {value} = event.target; + this.setState({isSearchActive: value !== ''}); + UIActions.setTorrentsSearchFilter(value); + }; + + handleResetClick = () => { + this.resetSearch(); + UIActions.setTorrentsSearchFilter(''); + }; + + resetSearch = () => { + this.setState((state) => ({ + inputFieldKey: state.inputFieldKey + 1, + isSearchActive: false, + })); + }; + + render() { + const {intl} = this.props; + const {inputFieldKey, isSearchActive} = this.state; + let clearSearchButton = null; + const classes = classnames({ + sidebar__item: true, // eslint-disable-line + search: true, + 'is-in-use': isSearchActive, + }); + + if (isSearchActive) { + clearSearchButton = ( + + ); + } + + return ( +
    + {clearSearchButton} + + +
    + ); + } +} + +export default injectIntl(SearchBox); diff --git a/client/src/javascript/components/sidebar/SearchTorrents.js b/client/src/javascript/components/sidebar/SearchTorrents.js deleted file mode 100644 index 25963005d..000000000 --- a/client/src/javascript/components/sidebar/SearchTorrents.js +++ /dev/null @@ -1,96 +0,0 @@ -import {injectIntl} from 'react-intl'; -import classnames from 'classnames'; -import React from 'react'; - -import Close from '../icons/Close'; -import connectStores from '../../util/connectStores'; -import EventTypes from '../../constants/EventTypes'; -import Search from '../icons/Search'; -import TorrentFilterStore from '../../stores/TorrentFilterStore'; -import UIActions from '../../actions/UIActions'; - -class SearchBox extends React.Component { - state = { - inputFieldKey: 0, - isSearchActive: false, - }; - - componentDidMount() { - TorrentFilterStore.listen(EventTypes.UI_TORRENTS_FILTER_CLEAR, this.resetSearch); - } - - componentWillUnmount() { - TorrentFilterStore.unlisten(EventTypes.UI_TORRENTS_FILTER_CLEAR, this.resetSearch); - } - - handleSearchChange = event => { - const {value} = event.target; - this.setState({isSearchActive: value !== ''}); - UIActions.setTorrentsSearchFilter(value); - }; - - handleResetClick = () => { - this.resetSearch(); - UIActions.setTorrentsSearchFilter(''); - }; - - resetSearch = () => { - this.setState(state => { - return { - inputFieldKey: state.inputFieldKey + 1, - isSearchActive: false, - }; - }); - }; - - render() { - const {inputFieldKey, isSearchActive} = this.state; - let clearSearchButton = null; - const classes = classnames({ - sidebar__item: true, - search: true, - 'is-in-use': isSearchActive, - }); - - if (isSearchActive) { - clearSearchButton = ( - - ); - } - - return ( -
    - {clearSearchButton} - - -
    - ); - } -} - -const ConnectedSearchBox = connectStores(injectIntl(SearchBox), () => { - return [ - { - store: TorrentFilterStore, - event: EventTypes.UI_TORRENTS_FILTER_SEARCH_CHANGE, - getValue: ({store}) => { - return { - searchValue: store.getSearchFilter(), - }; - }, - }, - ]; -}); - -export default ConnectedSearchBox; diff --git a/client/src/javascript/components/sidebar/SettingsButton.js b/client/src/javascript/components/sidebar/SettingsButton.js deleted file mode 100644 index 812a9ebe4..000000000 --- a/client/src/javascript/components/sidebar/SettingsButton.js +++ /dev/null @@ -1,54 +0,0 @@ -import {defineMessages, injectIntl} from 'react-intl'; -import React from 'react'; - -import SettingsIcon from '../icons/SettingsIcon'; -import Tooltip from '../general/Tooltip'; -import UIActions from '../../actions/UIActions'; - -const MESSAGES = defineMessages({ - settings: { - id: 'sidebar.button.settings', - defaultMessage: 'Settings', - }, -}); -const METHODS_TO_BIND = ['handleSettingsButtonClick']; - -class SettingsButton extends React.Component { - constructor() { - super(); - - this.tooltipRef = null; - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - handleSettingsButtonClick() { - if (this.tooltipRef != null) { - this.tooltipRef.dismissTooltip(); - } - - UIActions.displayModal({id: 'settings'}); - } - - render() { - const label = this.props.intl.formatMessage(MESSAGES.settings); - - return ( - { - this.tooltipRef = ref; - }} - position="bottom" - wrapperClassName="sidebar__action sidebar__icon-button - sidebar__icon-button--interactive tooltip__wrapper"> - - - ); - } -} - -export default injectIntl(SettingsButton); diff --git a/client/src/javascript/components/sidebar/SettingsButton.tsx b/client/src/javascript/components/sidebar/SettingsButton.tsx new file mode 100644 index 000000000..f8a83f590 --- /dev/null +++ b/client/src/javascript/components/sidebar/SettingsButton.tsx @@ -0,0 +1,38 @@ +import {defineMessages, useIntl} from 'react-intl'; +import {FC, useRef} from 'react'; + +import SettingsIcon from '../icons/SettingsIcon'; +import Tooltip from '../general/Tooltip'; +import UIActions from '../../actions/UIActions'; + +const MESSAGES = defineMessages({ + settings: { + id: 'sidebar.button.settings', + }, +}); + +const SettingsButton: FC = () => { + const intl = useIntl(); + const label = intl.formatMessage(MESSAGES.settings); + const tooltipRef = useRef(null); + + return ( + { + if (tooltipRef.current != null) { + tooltipRef.current.dismissTooltip(); + } + + UIActions.displayModal({id: 'settings'}); + }} + ref={tooltipRef} + position="bottom" + wrapperClassName="sidebar__action sidebar__icon-button + sidebar__icon-button--interactive tooltip__wrapper"> + + + ); +}; + +export default SettingsButton; diff --git a/client/src/javascript/components/sidebar/Sidebar.js b/client/src/javascript/components/sidebar/Sidebar.js deleted file mode 100644 index 6fab9b41a..000000000 --- a/client/src/javascript/components/sidebar/Sidebar.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; - -import ClientStats from './TransferData'; -import CustomScrollbars from '../general/CustomScrollbars'; -import FeedsButton from './FeedsButton'; -import LogoutButton from './LogoutButton'; -import NotificationsButton from './NotificationsButton'; -import SearchTorrents from './SearchTorrents'; -import SettingsButton from './SettingsButton'; -import SidebarActions from './SidebarActions'; -import SpeedLimitDropdown from './SpeedLimitDropdown'; -import StatusFilters from './StatusFilters'; -import TagFilters from './TagFilters'; -import TrackerFilters from './TrackerFilters'; -import DiskUsage from './DiskUsage'; - -const Sidebar = () => { - return ( - - - - - - - - - - - - - - - - ); -}; - -export default Sidebar; diff --git a/client/src/javascript/components/sidebar/Sidebar.tsx b/client/src/javascript/components/sidebar/Sidebar.tsx new file mode 100644 index 000000000..47b81ea4f --- /dev/null +++ b/client/src/javascript/components/sidebar/Sidebar.tsx @@ -0,0 +1,48 @@ +import {FC} from 'react'; +import {OverlayScrollbarsComponent} from 'overlayscrollbars-react'; + +import DiskUsage from './DiskUsage'; +import FeedsButton from './FeedsButton'; +import LogoutButton from './LogoutButton'; +import NotificationsButton from './NotificationsButton'; +import SearchBox from './SearchBox'; +import SettingsButton from './SettingsButton'; +import SidebarActions from './SidebarActions'; +import SpeedLimitDropdown from './SpeedLimitDropdown'; +import StatusFilters from './StatusFilters'; +import TagFilters from './TagFilters'; +import ThemeSwitchButton from './ThemeSwitchButton'; +import TrackerFilters from './TrackerFilters'; +import TransferData from './TransferData'; + +const Sidebar: FC = () => ( + + + + + + + + + + + + + + +
    + + + + +); + +export default Sidebar; diff --git a/client/src/javascript/components/sidebar/SidebarActions.js b/client/src/javascript/components/sidebar/SidebarActions.js deleted file mode 100644 index c43319f2e..000000000 --- a/client/src/javascript/components/sidebar/SidebarActions.js +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; - -class SidebarActions extends React.Component { - render() { - return
    {this.props.children}
    ; - } -} - -export default SidebarActions; diff --git a/client/src/javascript/components/sidebar/SidebarActions.tsx b/client/src/javascript/components/sidebar/SidebarActions.tsx new file mode 100644 index 000000000..5801841b7 --- /dev/null +++ b/client/src/javascript/components/sidebar/SidebarActions.tsx @@ -0,0 +1,11 @@ +import {FC, ReactNode} from 'react'; + +interface SidebarActionsProps { + children: ReactNode; +} + +const SidebarActions: FC = ({children}: SidebarActionsProps) => ( +
    {children}
    +); + +export default SidebarActions; diff --git a/client/src/javascript/components/sidebar/SidebarFilter.js b/client/src/javascript/components/sidebar/SidebarFilter.js deleted file mode 100644 index 7156bb4cb..000000000 --- a/client/src/javascript/components/sidebar/SidebarFilter.js +++ /dev/null @@ -1,50 +0,0 @@ -import classnames from 'classnames'; -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import Badge from '../general/Badge'; - -const METHODS_TO_BIND = ['handleClick']; - -class SidebarFilter extends React.Component { - constructor() { - super(); - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - handleClick() { - this.props.handleClick(this.props.slug); - } - - render() { - const classNames = classnames('sidebar-filter__item', { - 'is-active': this.props.isActive, - }); - let {name} = this.props; - - if (this.props.name === 'all') { - name = this.props.intl.formatMessage({ - id: 'filter.all', - defaultMessage: 'All', - }); - } else if (this.props.name === 'untagged') { - name = this.props.intl.formatMessage({ - id: 'filter.untagged', - defaultMessage: 'Untagged', - }); - } - - return ( -
  • - {this.props.icon} - {name} - {this.props.count} -
  • - ); - } -} - -export default injectIntl(SidebarFilter); diff --git a/client/src/javascript/components/sidebar/SidebarFilter.tsx b/client/src/javascript/components/sidebar/SidebarFilter.tsx new file mode 100644 index 000000000..efe5945d7 --- /dev/null +++ b/client/src/javascript/components/sidebar/SidebarFilter.tsx @@ -0,0 +1,57 @@ +import classnames from 'classnames'; +import {FC} from 'react'; +import {useIntl} from 'react-intl'; + +import Badge from '../general/Badge'; + +interface SidebarFilterProps { + name: string; + icon?: JSX.Element; + isActive: boolean; + slug: string; + count: number; + handleClick: (slug: string) => void; +} + +const SidebarFilter: FC = (props: SidebarFilterProps) => { + const {isActive, count, slug, icon, handleClick} = props; + const intl = useIntl(); + + const classNames = classnames('sidebar-filter__item', { + 'is-active': isActive, + }); + let {name} = props; + + if (name === '') { + name = intl.formatMessage({ + id: 'filter.all', + }); + } else if (name === 'untagged') { + if (count === 0) { + return null; + } + name = intl.formatMessage({ + id: 'filter.untagged', + }); + } + + if (slug === 'checking' || slug === 'error') { + if (count === 0) { + return null; + } + } + + return ( +
  • handleClick(slug)}> + {icon} + {name} + {count} +
  • + ); +}; + +SidebarFilter.defaultProps = { + icon: undefined, +}; + +export default SidebarFilter; diff --git a/client/src/javascript/components/sidebar/SidebarItem.js b/client/src/javascript/components/sidebar/SidebarItem.js deleted file mode 100644 index 3547635ee..000000000 --- a/client/src/javascript/components/sidebar/SidebarItem.js +++ /dev/null @@ -1,25 +0,0 @@ -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -class SidebarItem extends React.Component { - static propTypes = { - baseClassName: PropTypes.string, - children: PropTypes.node, - modifier: PropTypes.string, - }; - - static defaultProps = { - baseClassName: 'sidebar__item', - }; - - render() { - const classes = classnames(this.props.baseClassName, { - [`${this.props.baseClassName}--${this.props.modifier}`]: this.props.modifier, - }); - - return
    {this.props.children}
    ; - } -} - -export default SidebarItem; diff --git a/client/src/javascript/components/sidebar/SidebarItem.tsx b/client/src/javascript/components/sidebar/SidebarItem.tsx new file mode 100644 index 000000000..35af93792 --- /dev/null +++ b/client/src/javascript/components/sidebar/SidebarItem.tsx @@ -0,0 +1,22 @@ +import classnames from 'classnames'; +import {FC, ReactNode} from 'react'; + +interface SidebarItemProps { + children: ReactNode; + baseClassName?: string; + modifier: string; +} + +const SidebarItem: FC = ({children, baseClassName, modifier}: SidebarItemProps) => { + const classes = classnames(baseClassName, { + [`${baseClassName}--${modifier}`]: modifier, + }); + + return
    {children}
    ; +}; + +SidebarItem.defaultProps = { + baseClassName: 'sidebar__item', +}; + +export default SidebarItem; diff --git a/client/src/javascript/components/sidebar/SpeedLimitDropdown.js b/client/src/javascript/components/sidebar/SpeedLimitDropdown.js deleted file mode 100644 index 9724026fe..000000000 --- a/client/src/javascript/components/sidebar/SpeedLimitDropdown.js +++ /dev/null @@ -1,171 +0,0 @@ -import _ from 'lodash'; -import {defineMessages, FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import ClientActions from '../../actions/ClientActions'; -import connectStores from '../../util/connectStores'; -import Dropdown from '../general/form-elements/Dropdown'; -import EventTypes from '../../constants/EventTypes'; -import LimitsIcon from '../icons/Limits'; -import SettingsStore from '../../stores/SettingsStore'; -import Size from '../general/Size'; -import Tooltip from '../general/Tooltip'; -import TransferDataStore from '../../stores/TransferDataStore'; - -const MESSAGES = defineMessages({ - speedLimits: { - defaultMessage: 'Speed Limits', - id: 'sidebar.button.speedlimits', - }, - unlimited: { - defaultMessage: 'Unlimited', - id: 'speed.unlimited', - }, -}); - -class SpeedLimitDropdown extends React.Component { - tooltipRef = null; - - getDropdownHeader() { - return ( - - ); - } - - getDropdownTrigger() { - const label = this.props.intl.formatMessage(MESSAGES.speedLimits); - - return ( - { - this.tooltipRef = node; - }} - wrapperClassName="sidebar__icon-button tooltip__wrapper"> - - - ); - } - - getHumanReadableSpeed(bytes) { - if (bytes === 0) { - return this.props.intl.formatMessage(MESSAGES.unlimited); - } - return ; - } - - getSpeedList(property) { - const heading = { - className: `dropdown__label dropdown__label--${property}`, - displayName: `${property.charAt(0).toUpperCase()}${property.slice(1)}`, - selectable: false, - value: null, - }; - - let insertCurrentThrottle = true; - const currentThrottle = this.props.currentThrottles; - const speeds = this.props.speedLimits[property]; - - const items = speeds.map(bytes => { - let selected = false; - bytes = Number(bytes); - - // Check if the current throttle setting exists in the preset speeds list. - // Determine if we need to add the current throttle setting to the menu. - if (currentThrottle && currentThrottle[property] === bytes) { - selected = true; - insertCurrentThrottle = false; - } - - return { - displayName: this.getHumanReadableSpeed(bytes), - property, - selected, - selectable: true, - value: bytes, - }; - }); - - // If the current throttle setting doesn't exist in the pre-set speeds list, - // add it and show it as selected. - if (insertCurrentThrottle && currentThrottle) { - // Find the position to insert the current throttle setting so that it - // remains sorted from lowest to highest. - const insertionPoint = _.sortedIndex(speeds, currentThrottle[property]); - - items.splice(insertionPoint, 0, { - displayName: this.getHumanReadableSpeed(currentThrottle[property]), - property, - selected: true, - selectable: true, - value: currentThrottle[property], - }); - } - - items.unshift(heading); - - return items; - } - - getDropdownMenus() { - return [this.getSpeedList('download'), this.getSpeedList('upload')]; - } - - handleDropdownOpen = () => { - this.tooltipRef.dismissTooltip(); - }; - - handleItemSelect(data) { - ClientActions.setThrottle(data.property, data.value); - } - - render() { - return ( - - ); - } -} - -const ConnectedSpeedLimitDropdown = connectStores(injectIntl(SpeedLimitDropdown), () => { - return [ - { - store: SettingsStore, - event: EventTypes.SETTINGS_CHANGE, - getValue: ({store}) => { - return { - speedLimits: store.getFloodSettings('speedLimits'), - }; - }, - }, - { - store: TransferDataStore, - event: EventTypes.CLIENT_TRANSFER_SUMMARY_CHANGE, - getValue: ({store}) => { - const transferSummary = store.getTransferSummary(); - - return { - currentThrottles: { - upload: transferSummary.upThrottle, - download: transferSummary.downThrottle, - }, - }; - }, - }, - ]; -}); - -export default ConnectedSpeedLimitDropdown; diff --git a/client/src/javascript/components/sidebar/SpeedLimitDropdown.tsx b/client/src/javascript/components/sidebar/SpeedLimitDropdown.tsx new file mode 100644 index 000000000..61d216b8b --- /dev/null +++ b/client/src/javascript/components/sidebar/SpeedLimitDropdown.tsx @@ -0,0 +1,167 @@ +import {defineMessages, FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; +import {observer} from 'mobx-react'; +import sortedIndex from 'lodash/sortedIndex'; +import * as React from 'react'; + +import type {TransferDirection} from '@shared/types/TransferData'; + +import ClientActions from '../../actions/ClientActions'; +import Dropdown from '../general/form-elements/Dropdown'; +import LimitsIcon from '../icons/Limits'; +import SettingStore from '../../stores/SettingStore'; +import Size from '../general/Size'; +import Tooltip from '../general/Tooltip'; + +import type {DropdownItem} from '../general/form-elements/Dropdown'; + +const MESSAGES = defineMessages({ + speedLimits: { + id: 'sidebar.button.speedlimits', + }, + download: { + id: 'sidebar.speedlimits.download', + }, + upload: { + id: 'sidebar.speedlimits.upload', + }, + unlimited: { + id: 'speed.unlimited', + }, +}); + +@observer +class SpeedLimitDropdown extends React.Component { + static handleItemSelect(item: DropdownItem) { + if (item.value != null) { + if (item.property === 'download') { + ClientActions.saveSetting('throttleGlobalDownSpeed', item.value); + } else if (item.property === 'upload') { + ClientActions.saveSetting('throttleGlobalUpSpeed', item.value); + } + } + } + + tooltipRef: Tooltip | null = null; + + getDropdownHeader(): React.ReactNode { + return ( + + ); + } + + getDropdownTrigger(): React.ReactNode { + const label = this.props.intl.formatMessage(MESSAGES.speedLimits); + + return ( + { + this.tooltipRef = node; + }} + wrapperClassName="sidebar__icon-button tooltip__wrapper"> + + + ); + } + + getHumanReadableSpeed(bytes: number): React.ReactNode { + if (bytes === 0) { + return this.props.intl.formatMessage(MESSAGES.unlimited); + } + return ; + } + + getSpeedList(direction: TransferDirection): Array> { + const {speedLimits} = SettingStore.floodSettings; + const {throttleGlobalDownSpeed = 0, throttleGlobalUpSpeed = 0} = SettingStore.clientSettings || {}; + + const heading = { + className: `dropdown__label dropdown__label--${direction}`, + ...(direction === 'download' + ? {displayName: this.props.intl.formatMessage(MESSAGES.download)} + : {displayName: this.props.intl.formatMessage(MESSAGES.upload)}), + selectable: false, + value: null, + }; + + const currentThrottle: Record = { + download: throttleGlobalDownSpeed, + upload: throttleGlobalUpSpeed, + }; + + const speeds: number[] = speedLimits[direction]; + + let insertCurrentThrottle = true; + const items: Array> = speeds.map((bytes) => { + let selected = false; + + // Check if the current throttle setting exists in the preset speeds list. + // Determine if we need to add the current throttle setting to the menu. + if (currentThrottle && currentThrottle[direction] === bytes) { + selected = true; + insertCurrentThrottle = false; + } + + return { + displayName: this.getHumanReadableSpeed(bytes), + property: direction, + selected, + selectable: true, + value: bytes, + }; + }); + + // If the current throttle setting doesn't exist in the pre-set speeds list, + // add it and show it as selected. + if (insertCurrentThrottle && currentThrottle) { + // Find the position to insert the current throttle setting so that it + // remains sorted from lowest to highest. + const insertionPoint = sortedIndex(speeds, currentThrottle[direction]); + + items.splice(insertionPoint, 0, { + displayName: this.getHumanReadableSpeed(currentThrottle[direction]), + property: direction, + selected: true, + selectable: true, + value: currentThrottle[direction], + }); + } + + items.unshift(heading); + + return items; + } + + getDropdownMenus() { + return [this.getSpeedList('download'), this.getSpeedList('upload')]; + } + + handleDropdownOpen = () => { + if (this.tooltipRef != null) { + this.tooltipRef.dismissTooltip(); + } + }; + + render() { + return ( + + ); + } +} + +export default injectIntl(SpeedLimitDropdown); diff --git a/client/src/javascript/components/sidebar/StatusFilters.js b/client/src/javascript/components/sidebar/StatusFilters.js deleted file mode 100644 index 57aff4d88..000000000 --- a/client/src/javascript/components/sidebar/StatusFilters.js +++ /dev/null @@ -1,134 +0,0 @@ -import {FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import Active from '../icons/Active'; -import All from '../icons/All'; -import Completed from '../icons/Completed'; -import connectStores from '../../util/connectStores'; -import DownloadSmall from '../icons/DownloadSmall'; -import ErrorIcon from '../icons/ErrorIcon'; -import EventTypes from '../../constants/EventTypes'; -import Inactive from '../icons/Inactive'; -import SidebarFilter from './SidebarFilter'; -import StopIcon from '../icons/StopIcon'; -import TorrentFilterStore from '../../stores/TorrentFilterStore'; -import UIActions from '../../actions/UIActions'; - -class StatusFilters extends React.Component { - handleClick(filter) { - UIActions.setTorrentStatusFilter(filter); - } - - getFilters() { - const filters = [ - { - label: this.props.intl.formatMessage({ - id: 'filter.all', - defaultMessage: 'All', - }), - slug: 'all', - icon: , - }, - { - label: this.props.intl.formatMessage({ - id: 'filter.status.downloading', - defaultMessage: 'Downloading', - }), - slug: 'downloading', - icon: , - }, - { - label: this.props.intl.formatMessage({ - id: 'filter.status.completed', - defaultMessage: 'Complete', - }), - slug: 'complete', - icon: , - }, - { - label: this.props.intl.formatMessage({ - id: 'filter.status.stopped', - defaultMessage: 'Stopped', - }), - slug: 'stopped', - icon: , - }, - { - label: this.props.intl.formatMessage({ - id: 'filter.status.active', - defaultMessage: 'All', - }), - slug: 'active', - icon: , - }, - { - label: this.props.intl.formatMessage({ - id: 'filter.status.inactive', - defaultMessage: 'Inactive', - }), - slug: 'inactive', - icon: , - }, - { - label: this.props.intl.formatMessage({ - id: 'filter.status.error', - defaultMessage: 'Error', - }), - slug: 'error', - icon: , - }, - ]; - - const filterElements = filters.map(filter => ( - - )); - - return filterElements; - } - - render() { - const filters = this.getFilters(); - - return ( -
      -
    • - -
    • - {filters} -
    - ); - } -} - -const ConnectedStatusFilters = connectStores(injectIntl(StatusFilters), () => { - return [ - { - store: TorrentFilterStore, - event: EventTypes.CLIENT_FETCH_TORRENT_TAXONOMY_SUCCESS, - getValue: ({store}) => { - return { - statusCount: store.getTorrentStatusCount(), - }; - }, - }, - { - store: TorrentFilterStore, - event: EventTypes.UI_TORRENTS_FILTER_STATUS_CHANGE, - getValue: ({store}) => { - return { - statusFilter: store.getStatusFilter(), - }; - }, - }, - ]; -}); - -export default ConnectedStatusFilters; diff --git a/client/src/javascript/components/sidebar/StatusFilters.tsx b/client/src/javascript/components/sidebar/StatusFilters.tsx new file mode 100644 index 000000000..3eaf10ce5 --- /dev/null +++ b/client/src/javascript/components/sidebar/StatusFilters.tsx @@ -0,0 +1,115 @@ +import {FC} from 'react'; +import {FormattedMessage, useIntl} from 'react-intl'; +import {observer} from 'mobx-react'; + +import type {TorrentStatus} from '@shared/constants/torrentStatusMap'; + +import Active from '../icons/Active'; +import All from '../icons/All'; +import Completed from '../icons/Completed'; +import DownloadSmall from '../icons/DownloadSmall'; +import ErrorIcon from '../icons/ErrorIcon'; +import Inactive from '../icons/Inactive'; +import SidebarFilter from './SidebarFilter'; +import StopIcon from '../icons/StopIcon'; +import SpinnerIcon from '../icons/SpinnerIcon'; +import TorrentFilterStore from '../../stores/TorrentFilterStore'; +import UIActions from '../../actions/UIActions'; +import UploadSmall from '../icons/UploadSmall'; + +const StatusFilters: FC = observer(() => { + const intl = useIntl(); + + const filters: Array<{ + label: string; + slug: TorrentStatus | ''; + icon: JSX.Element; + }> = [ + { + label: intl.formatMessage({ + id: 'filter.all', + }), + slug: '', + icon: , + }, + { + label: intl.formatMessage({ + id: 'filter.status.downloading', + }), + slug: 'downloading', + icon: , + }, + { + label: intl.formatMessage({ + id: 'filter.status.seeding', + }), + slug: 'seeding', + icon: , + }, + { + label: intl.formatMessage({ + id: 'filter.status.checking', + }), + slug: 'checking', + icon: , + }, + { + label: intl.formatMessage({ + id: 'filter.status.completed', + }), + slug: 'complete', + icon: , + }, + { + label: intl.formatMessage({ + id: 'filter.status.stopped', + }), + slug: 'stopped', + icon: , + }, + { + label: intl.formatMessage({ + id: 'filter.status.active', + }), + slug: 'active', + icon: , + }, + { + label: intl.formatMessage({ + id: 'filter.status.inactive', + }), + slug: 'inactive', + icon: , + }, + { + label: intl.formatMessage({ + id: 'filter.status.error', + }), + slug: 'error', + icon: , + }, + ]; + + const filterElements = filters.map((filter) => ( + UIActions.setTorrentStatusFilter(selection as TorrentStatus)} + count={TorrentFilterStore.taxonomy.statusCounts[filter.slug] || 0} + key={filter.slug} + icon={filter.icon} + isActive={filter.slug === TorrentFilterStore.filters.statusFilter} + name={filter.label} + slug={filter.slug} + /> + )); + + return ( +
      +
    • + +
    • + {filterElements} +
    + ); +}); + +export default StatusFilters; diff --git a/client/src/javascript/components/sidebar/TagFilters.js b/client/src/javascript/components/sidebar/TagFilters.js deleted file mode 100644 index ff1352b20..000000000 --- a/client/src/javascript/components/sidebar/TagFilters.js +++ /dev/null @@ -1,86 +0,0 @@ -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -import connectStores from '../../util/connectStores'; -import EventTypes from '../../constants/EventTypes'; -import SidebarFilter from './SidebarFilter'; -import TorrentFilterStore from '../../stores/TorrentFilterStore'; -import UIActions from '../../actions/UIActions'; - -class TagFilters extends React.Component { - getFilters() { - const filterItems = Object.keys(this.props.tagCount).sort((a, b) => { - if (a === 'all' || a === 'untagged') { - return -1; - } - if (b === 'all' || b === 'untagged') { - return 1; - } - - return a.localeCompare(b); - }); - - const filterElements = filterItems.map(filter => ( - - )); - - return filterElements; - } - - handleClick(filter) { - UIActions.setTorrentTagFilter(filter); - } - - hasTags() { - const tags = Object.keys(this.props.tagCount); - - return !((tags.length === 1 && tags[0] === 'all') || (tags.length === 2 && tags[1] === 'untagged')); - } - - render() { - if (!this.hasTags()) { - return null; - } - - return ( -
      -
    • - -
    • - {this.getFilters()} -
    - ); - } -} - -const ConnectedTagFilters = connectStores(TagFilters, () => { - return [ - { - store: TorrentFilterStore, - event: EventTypes.CLIENT_FETCH_TORRENT_TAXONOMY_SUCCESS, - getValue: ({store}) => { - return { - tagCount: store.getTorrentTagCount(), - }; - }, - }, - { - store: TorrentFilterStore, - event: EventTypes.UI_TORRENTS_FILTER_TAG_CHANGE, - getValue: ({store}) => { - return { - tagFilter: store.getTagFilter(), - }; - }, - }, - ]; -}); - -export default ConnectedTagFilters; diff --git a/client/src/javascript/components/sidebar/TagFilters.tsx b/client/src/javascript/components/sidebar/TagFilters.tsx new file mode 100644 index 000000000..abd53da67 --- /dev/null +++ b/client/src/javascript/components/sidebar/TagFilters.tsx @@ -0,0 +1,49 @@ +import {FC} from 'react'; +import {FormattedMessage} from 'react-intl'; +import {observer} from 'mobx-react'; + +import SidebarFilter from './SidebarFilter'; +import TorrentFilterStore from '../../stores/TorrentFilterStore'; +import UIActions from '../../actions/UIActions'; + +const TagFilters: FC = observer(() => { + const tags = Object.keys(TorrentFilterStore.taxonomy.tagCounts); + + if ((tags.length === 1 && tags[0] === '') || (tags.length === 2 && tags[1] === 'untagged')) { + return null; + } + + const filterItems = tags.slice().sort((a, b) => { + if (a === '' || a === 'untagged') { + return -1; + } + + if (b === '' || b === 'untagged') { + return 1; + } + + return a.localeCompare(b); + }); + + const filterElements = filterItems.map((filter) => ( + + )); + + return ( +
      +
    • + +
    • + {filterElements} +
    + ); +}); + +export default TagFilters; diff --git a/client/src/javascript/components/sidebar/ThemeSwitchButton.tsx b/client/src/javascript/components/sidebar/ThemeSwitchButton.tsx new file mode 100644 index 000000000..e019d85d9 --- /dev/null +++ b/client/src/javascript/components/sidebar/ThemeSwitchButton.tsx @@ -0,0 +1,25 @@ +import {FC} from 'react'; +import {useIntl} from 'react-intl'; + +import ConfigStore from '../../stores/ConfigStore'; +import ThemeSwitchIcon from '../icons/ThemeSwitchIcon'; +import Tooltip from '../general/Tooltip'; + +const ThemeSwitchButton: FC = () => { + const intl = useIntl(); + + return ( + ConfigStore.setUserPreferDark(!ConfigStore.preferDark)} + position="bottom" + wrapperClassName="sidebar__action sidebar__icon-button + sidebar__icon-button--interactive tooltip__wrapper"> + + + ); +}; + +export default ThemeSwitchButton; diff --git a/client/src/javascript/components/sidebar/TrackerFilters.js b/client/src/javascript/components/sidebar/TrackerFilters.js deleted file mode 100644 index 6dfa5b833..000000000 --- a/client/src/javascript/components/sidebar/TrackerFilters.js +++ /dev/null @@ -1,88 +0,0 @@ -import {FormattedMessage} from 'react-intl'; -import React from 'react'; - -import connectStores from '../../util/connectStores'; -import EventTypes from '../../constants/EventTypes'; -import SidebarFilter from './SidebarFilter'; -import TorrentFilterStore from '../../stores/TorrentFilterStore'; -import UIActions from '../../actions/UIActions'; - -class TrackerFilters extends React.Component { - getFilters() { - const filterItems = Object.keys(this.props.trackerCount).sort((a, b) => { - if (a === 'all') { - return -1; - } - if (b === 'all') { - return 1; - } - - return a.localeCompare(b); - }); - - const filterElements = filterItems.map(filter => ( - - )); - - return filterElements; - } - - handleClick(filter) { - UIActions.setTorrentTrackerFilter(filter); - } - - hasTrackers() { - const trackers = Object.keys(this.props.trackerCount); - - return !(trackers.length === 1 && trackers[0] === 'all'); - } - - render() { - const filters = this.getFilters(); - - if (!this.hasTrackers()) { - return null; - } - - return ( -
      -
    • - -
    • - {filters} -
    - ); - } -} - -const ConnectedTrackerFilters = connectStores(TrackerFilters, () => { - return [ - { - store: TorrentFilterStore, - event: EventTypes.CLIENT_FETCH_TORRENT_TAXONOMY_SUCCESS, - getValue: ({store}) => { - return { - trackerCount: store.getTorrentTrackerCount(), - }; - }, - }, - { - store: TorrentFilterStore, - event: EventTypes.UI_TORRENTS_FILTER_TRACKER_CHANGE, - getValue: ({store}) => { - return { - trackerFilter: store.getTrackerFilter(), - }; - }, - }, - ]; -}); - -export default ConnectedTrackerFilters; diff --git a/client/src/javascript/components/sidebar/TrackerFilters.tsx b/client/src/javascript/components/sidebar/TrackerFilters.tsx new file mode 100644 index 000000000..302a9469d --- /dev/null +++ b/client/src/javascript/components/sidebar/TrackerFilters.tsx @@ -0,0 +1,48 @@ +import {FC} from 'react'; +import {FormattedMessage} from 'react-intl'; +import {observer} from 'mobx-react'; + +import SidebarFilter from './SidebarFilter'; +import TorrentFilterStore from '../../stores/TorrentFilterStore'; +import UIActions from '../../actions/UIActions'; + +const TrackerFilters: FC = observer(() => { + const trackers = Object.keys(TorrentFilterStore.taxonomy.trackerCounts); + + if (trackers.length === 1 && trackers[0] === '') { + return null; + } + + const filterItems = trackers.slice().sort((a, b) => { + if (a === '') { + return -1; + } + if (b === '') { + return 1; + } + + return a.localeCompare(b); + }); + + const filterElements = filterItems.map((filter) => ( + + )); + + return ( +
      +
    • + +
    • + {filterElements} +
    + ); +}); + +export default TrackerFilters; diff --git a/client/src/javascript/components/sidebar/TransferData.js b/client/src/javascript/components/sidebar/TransferData.js deleted file mode 100644 index 6179b64fa..000000000 --- a/client/src/javascript/components/sidebar/TransferData.js +++ /dev/null @@ -1,106 +0,0 @@ -import React from 'react'; - -import ClientStatusStore from '../../stores/ClientStatusStore'; -import connectStores from '../../util/connectStores'; -import EventTypes from '../../constants/EventTypes'; -import TransferRateDetails from './TransferRateDetails'; -import TransferRateGraph from './TransferRateGraph'; - -class TransferData extends React.Component { - state = { - graphInspectorPoint: null, - sidebarWidth: 0, - }; - - componentDidMount() { - const wrapperNode = this.wrapperRef; - - if (wrapperNode != null) { - this.setState({ - sidebarWidth: wrapperNode.offsetWidth, - }); - } - } - - handleGraphHover = graphInspectorPoint => { - this.setState({graphInspectorPoint}); - }; - - handleGraphMouseOut = () => { - this.setState({graphInspectorPoint: null}); - }; - - handleMouseMove = event => { - if (this.props.isClientConnected && event && event.nativeEvent && event.nativeEvent.clientX != null) { - this.rateGraphRef.handleMouseMove(event.nativeEvent.clientX); - } - }; - - handleMouseOut = () => { - if (this.props.isClientConnected) { - this.rateGraphRef.handleMouseOut(); - } - }; - - handleMouseOver = () => { - if (this.props.isClientConnected) { - this.rateGraphRef.handleMouseOver(); - } - }; - - renderTransferRateGraph() { - if (!this.props.isClientConnected) return null; - - return ( - { - this.rateGraphRef = ref; - }} - width={this.state.sidebarWidth} - /> - ); - } - - render() { - return ( -
    { - this.wrapperRef = ref; - }}> -
    - - {this.renderTransferRateGraph()} -
    -
    - ); - } -} - -TransferData.defaultProps = { - historyLength: 1, -}; - -const ConnectedTransferData = connectStores(TransferData, () => { - return [ - { - store: ClientStatusStore, - event: EventTypes.CLIENT_CONNECTION_STATUS_CHANGE, - getValue: ({store}) => { - return { - isClientConnected: store.getIsConnected(), - }; - }, - }, - ]; -}); - -export default ConnectedTransferData; diff --git a/client/src/javascript/components/sidebar/TransferData.tsx b/client/src/javascript/components/sidebar/TransferData.tsx new file mode 100644 index 000000000..10a1d8111 --- /dev/null +++ b/client/src/javascript/components/sidebar/TransferData.tsx @@ -0,0 +1,108 @@ +import {observer} from 'mobx-react'; +import Measure from 'react-measure'; +import * as React from 'react'; + +import ClientStatusStore from '../../stores/ClientStatusStore'; +import TransferRateDetails from './TransferRateDetails'; +import TransferRateGraph from './TransferRateGraph'; + +import type {TransferRateGraphInspectorPoint} from './TransferRateGraph'; + +interface TransferDataStates { + graphInspectorPoint: TransferRateGraphInspectorPoint | null; + sidebarWidth: number; +} + +@observer +class TransferData extends React.Component { + rateGraphRef: TransferRateGraph | null = null; + + constructor(props: unknown) { + super(props); + + this.state = { + graphInspectorPoint: null, + sidebarWidth: 0, + }; + } + + handleGraphHover = (graphInspectorPoint: TransferDataStates['graphInspectorPoint']) => { + this.setState({graphInspectorPoint}); + }; + + handleGraphMouseOut = () => { + this.setState({graphInspectorPoint: null}); + }; + + handleMouseMove = (event: React.MouseEvent) => { + if ( + this.rateGraphRef != null && + ClientStatusStore.isConnected && + event && + event.nativeEvent && + event.nativeEvent.clientX != null + ) { + this.rateGraphRef.handleMouseMove(event.nativeEvent.clientX); + } + }; + + handleMouseOut = () => { + if (this.rateGraphRef != null && ClientStatusStore.isConnected) { + this.rateGraphRef.handleMouseOut(); + } + }; + + handleMouseOver = () => { + if (this.rateGraphRef != null && ClientStatusStore.isConnected) { + this.rateGraphRef.handleMouseOver(); + } + }; + + renderTransferRateGraph() { + const {sidebarWidth} = this.state; + + if (!ClientStatusStore.isConnected) return null; + + return ( + { + this.rateGraphRef = ref; + }} + width={sidebarWidth} + /> + ); + } + + render() { + const {graphInspectorPoint} = this.state; + + return ( + { + if (contentRect.offset != null) { + this.setState({sidebarWidth: contentRect.offset.width}); + } + }}> + {({measureRef}) => ( +
    +
    + + {this.renderTransferRateGraph()} +
    +
    + )} +
    + ); + } +} + +export default TransferData; diff --git a/client/src/javascript/components/sidebar/TransferRateDetails.js b/client/src/javascript/components/sidebar/TransferRateDetails.js deleted file mode 100644 index f65f11018..000000000 --- a/client/src/javascript/components/sidebar/TransferRateDetails.js +++ /dev/null @@ -1,137 +0,0 @@ -import classnames from 'classnames'; -import {defineMessages, injectIntl} from 'react-intl'; -import formatUtil from '@shared/util/formatUtil'; -import moment from 'moment'; -import React from 'react'; - -import ClientStatusStore from '../../stores/ClientStatusStore'; -import connectStores from '../../util/connectStores'; -import Download from '../icons/Download'; -import Duration from '../general/Duration'; -import EventTypes from '../../constants/EventTypes'; -import InfinityIcon from '../icons/InfinityIcon'; -import Size from '../general/Size'; -import TransferDataStore from '../../stores/TransferDataStore'; -import Upload from '../icons/Upload'; - -const messages = defineMessages({ - ago: { - id: 'general.ago', - defaultMessage: 'ago', - }, -}); - -const icons = { - download: , - infinity: , - upload: , -}; - -class TransferRateDetails extends React.Component { - getCurrentTansferRate(slug, options = {}) { - const {inspectorPoint, isClientConnected, transferSummary} = this.props; - - const throttles = { - download: transferSummary.downThrottle, - upload: transferSummary.upThrottle, - }; - let timestamp = null; - const transferTotals = { - download: transferSummary.downTotal, - upload: transferSummary.upTotal, - }; - - let transferRates = { - download: transferSummary.downRate, - upload: transferSummary.upRate, - }; - - if (inspectorPoint != null) { - transferRates = { - upload: inspectorPoint.uploadSpeed, - download: inspectorPoint.downloadSpeed, - }; - } - - const secondaryDataClasses = classnames('client-stats__rate__data--secondary', { - 'is-visible': inspectorPoint == null && isClientConnected, - }); - - const timestampClasses = classnames('client-stats__rate__data--timestamp', { - 'is-visible': inspectorPoint != null && options.showHoverDuration, - }); - - if (inspectorPoint != null && inspectorPoint.nearestTimestamp != null) { - const currentTime = moment(Date.now()); - const durationSummary = formatUtil.secondsToDuration( - moment.duration(currentTime.diff(moment(inspectorPoint.nearestTimestamp))).asSeconds(), - ); - - timestamp = ( -
    - -
    - ); - } - - let limit = null; - - if (throttles[slug] === 0) { - limit = icons.infinity; - } else { - limit = ; - } - - return ( -
    -
    {icons[slug]}
    -
    -
    - -
    - {timestamp} -
    -
    - -
    -
    {limit}
    -
    -
    -
    - ); - } - - render() { - return ( -
    - {this.getCurrentTansferRate('download', {showHoverDuration: true})} - {this.getCurrentTansferRate('upload')} -
    - ); - } -} - -const ConnectedTransferRateDetails = connectStores(injectIntl(TransferRateDetails), () => { - return [ - { - store: ClientStatusStore, - event: EventTypes.CLIENT_CONNECTION_STATUS_CHANGE, - getValue: ({store}) => { - return { - isClientConnected: store.getIsConnected(), - }; - }, - }, - { - store: TransferDataStore, - event: EventTypes.CLIENT_TRANSFER_SUMMARY_CHANGE, - getValue: ({store}) => { - return { - transferSummary: store.getTransferSummary(), - }; - }, - }, - ]; -}); - -export default ConnectedTransferRateDetails; diff --git a/client/src/javascript/components/sidebar/TransferRateDetails.tsx b/client/src/javascript/components/sidebar/TransferRateDetails.tsx new file mode 100644 index 000000000..a0389a07a --- /dev/null +++ b/client/src/javascript/components/sidebar/TransferRateDetails.tsx @@ -0,0 +1,119 @@ +import classnames from 'classnames'; +import {defineMessages, useIntl} from 'react-intl'; +import {FC} from 'react'; +import {observer} from 'mobx-react'; + +import type {TransferDirection} from '@shared/types/TransferData'; + +import ClientStatusStore from '../../stores/ClientStatusStore'; +import Download from '../icons/Download'; +import Duration from '../general/Duration'; +import InfinityIcon from '../icons/InfinityIcon'; +import SettingStore from '../../stores/SettingStore'; +import Size from '../general/Size'; +import TransferDataStore from '../../stores/TransferDataStore'; +import Upload from '../icons/Upload'; + +import type {TransferRateGraphInspectorPoint} from './TransferRateGraph'; + +const messages = defineMessages({ + ago: { + id: 'general.ago', + }, +}); + +const icons = { + download: , + infinity: , + upload: , +}; + +interface TransferRateDetailsProps { + inspectorPoint: TransferRateGraphInspectorPoint | null; +} + +const TransferRateDetails: FC = observer(({inspectorPoint}: TransferRateDetailsProps) => { + const intl = useIntl(); + + const getCurrentTransferRate = (direction: TransferDirection, options: {showHoverDuration?: boolean} = {}) => { + const {throttleGlobalDownSpeed = 0, throttleGlobalUpSpeed = 0} = SettingStore.clientSettings || {}; + const {transferSummary} = TransferDataStore; + + const throttles = { + download: throttleGlobalDownSpeed, + upload: throttleGlobalUpSpeed, + }; + + const transferTotals = { + download: transferSummary.downTotal, + upload: transferSummary.upTotal, + }; + + let transferRates = { + download: transferSummary.downRate, + upload: transferSummary.upRate, + }; + + if (inspectorPoint != null) { + transferRates = { + upload: inspectorPoint.uploadSpeed, + download: inspectorPoint.downloadSpeed, + }; + } + + const secondaryDataClasses = classnames('client-stats__rate__data--secondary', { + 'is-visible': inspectorPoint == null && ClientStatusStore.isConnected, + }); + + const timestampClasses = classnames('client-stats__rate__data--timestamp', { + 'is-visible': inspectorPoint != null && options.showHoverDuration, + }); + + let timestamp = null; + if (inspectorPoint?.nearestTimestamp != null) { + timestamp = ( +
    + +
    + ); + } + + let limit = null; + + if (throttles[direction] === 0) { + limit = icons.infinity; + } else { + limit = ; + } + + return ( +
    +
    {icons[direction]}
    +
    +
    + +
    + {timestamp} +
    +
    + +
    +
    {limit}
    +
    +
    +
    + ); + }; + + return ( +
    + {getCurrentTransferRate('download', {showHoverDuration: true})} + {getCurrentTransferRate('upload')} +
    + ); +}); + +export default TransferRateDetails; diff --git a/client/src/javascript/components/sidebar/TransferRateGraph.js b/client/src/javascript/components/sidebar/TransferRateGraph.js deleted file mode 100644 index 034b9482c..000000000 --- a/client/src/javascript/components/sidebar/TransferRateGraph.js +++ /dev/null @@ -1,243 +0,0 @@ -import d3 from 'd3'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import EventTypes from '../../constants/EventTypes'; -import TransferDataStore from '../../stores/TransferDataStore'; - -const METHODS_TO_BIND = [ - 'appendGraphCircles', - 'appendEmptyGraphShapes', - 'handleTransferHistoryChange', - 'handleMouseOut', - 'handleMouseOver', - 'handleMouseMove', - 'renderGraphData', -]; - -class TransferRateGraph extends React.Component { - static propTypes = { - width: PropTypes.number, - }; - - static defaultProps = { - width: 240, - }; - - constructor() { - super(); - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - - this.graphRefs = {areDefined: false, isHovered: false}; - this.isInitialRender = true; - this.lastMouseX = null; - this.shouldUpdateGraph = true; - this.xScale = {}; - this.yScale = {}; - } - - componentDidMount() { - TransferDataStore.listen(EventTypes.CLIENT_TRANSFER_HISTORY_REQUEST_SUCCESS, this.handleTransferHistoryChange); - - this.renderGraphData(); - } - - componentDidUpdate() { - this.renderGraphData(); - } - - componentWillUnmount() { - TransferDataStore.unlisten(EventTypes.CLIENT_TRANSFER_HISTORY_REQUEST_SUCCESS, this.handleTransferHistoryChange); - } - - appendGraphCircles(graph, slug) { - this.graphRefs[slug].inspectPoint = graph - .append('circle') - .attr('class', `graph__circle graph__circle--${slug}`) - .attr('r', 2.5); - } - - appendEmptyGraphShapes(graph, slug) { - if (this.graphRefs[slug] == null) { - this.graphRefs[slug] = {}; - } - - this.graphRefs[slug].area = graph - .append('path') - .attr('class', 'graph__area') - .attr('fill', `url('#graph__gradient--${slug}')`); - } - - appendEmptyGraphLines(graph, slug) { - this.graphRefs[slug].rateLine = graph.append('path').attr('class', `graph__line graph__line--${slug}`); - } - - getGradient(slug) { - return ( - - - - - ); - } - - handleTransferHistoryChange() { - this.updateGraph(); - } - - handleMouseMove(mouseX) { - this.lastMouseX = mouseX; - this.renderPrecisePointInspectors(); - } - - handleMouseOut() { - const {graphRefs, props} = this; - - if (graphRefs.areDefined) { - graphRefs.isHovered = false; - graphRefs.upload.inspectPoint.style('opacity', 0); - graphRefs.download.inspectPoint.style('opacity', 0); - } - - if (props.onMouseOut) { - props.onMouseOut(); - } - } - - handleMouseOver() { - this.graphRefs.isHovered = true; - this.graphRefs.upload.inspectPoint.style('opacity', 1); - this.graphRefs.download.inspectPoint.style('opacity', 1); - } - - setInspectorCoordinates(slug, hoverPoint) { - const { - graphRefs: { - [slug]: {inspectPoint}, - }, - xScale, - yScale, - } = this; - - const historicalData = TransferDataStore.getTransferRates(); - const upperSpeed = historicalData[slug][Math.ceil(hoverPoint)]; - const lowerSpeed = historicalData[slug][Math.floor(hoverPoint)]; - - const delta = upperSpeed - lowerSpeed; - const speedAtHoverPoint = lowerSpeed + delta * (hoverPoint % 1); - - const coordinates = {x: xScale(hoverPoint), y: yScale(speedAtHoverPoint)}; - - inspectPoint.attr('transform', `translate(${coordinates.x},${coordinates.y})`); - - return speedAtHoverPoint; - } - - updateGraph() { - this.renderGraphData(); - - if (this.graphRefs.isHovered) { - this.renderPrecisePointInspectors(); - } - } - - renderGraphData() { - const historicalData = TransferDataStore.getTransferRates(); - const {height, id, width} = this.props; - const graph = d3.select(`#${id}`); - const margin = {bottom: 10, top: 10}; - - this.xScale = d3.scale - .linear() - .domain([0, historicalData.download.length - 1]) - .range([0, width]); - - this.yScale = d3.scale - .linear() - .domain([ - 0, - d3.max(historicalData.download, (dataPoint, index) => Math.max(dataPoint, historicalData.upload[index])), - ]) - .range([height - margin.top, margin.bottom]); - - const lineFunc = interpolation => - d3.svg - .line() - .x((dataPoint, index) => this.xScale(index)) - .y(dataPoint => this.yScale(dataPoint)) - .interpolate(interpolation); - - const areaFunc = interpolation => - d3.svg - .area() - .x((dataPoint, index) => this.xScale(index)) - .y0(height) - .y1(dataPoint => this.yScale(dataPoint)) - .interpolate(interpolation); - - const interpolation = 'monotone'; - const downloadLinePath = lineFunc(interpolation)(historicalData.download); - const downloadAreaShape = areaFunc(interpolation)(historicalData.download); - const uploadLinePath = lineFunc(interpolation)(historicalData.upload); - const uploadAreaShape = areaFunc(interpolation)(historicalData.upload); - - if (!this.graphRefs.areDefined) { - this.appendEmptyGraphShapes(graph, 'download'); - this.appendEmptyGraphShapes(graph, 'upload'); - this.appendEmptyGraphLines(graph, 'download'); - this.appendEmptyGraphLines(graph, 'upload'); - this.appendGraphCircles(graph, 'download'); - this.appendGraphCircles(graph, 'upload'); - - this.graphRefs.areDefined = true; - } - - this.graphRefs.download.area.attr('d', downloadAreaShape); - this.graphRefs.download.rateLine.attr('d', downloadLinePath); - this.graphRefs.upload.area.attr('d', uploadAreaShape); - this.graphRefs.upload.rateLine.attr('d', uploadLinePath); - } - - renderPrecisePointInspectors() { - const { - lastMouseX, - props: {onHover}, - xScale, - } = this; - - const historicalData = TransferDataStore.getTransferRates(); - const hoverPoint = xScale.invert(lastMouseX); - const uploadSpeed = this.setInspectorCoordinates('upload', hoverPoint); - const downloadSpeed = this.setInspectorCoordinates('download', hoverPoint); - const nearestTimestamp = historicalData.timestamps[Math.round(hoverPoint)]; - - if (onHover) { - onHover({ - uploadSpeed, - downloadSpeed, - nearestTimestamp, - }); - } - } - - render() { - return ( - { - this.graphRefs.graph = ref; - }}> - - {this.getGradient('upload')} - {this.getGradient('download')} - - - ); - } -} - -export default TransferRateGraph; diff --git a/client/src/javascript/components/sidebar/TransferRateGraph.tsx b/client/src/javascript/components/sidebar/TransferRateGraph.tsx new file mode 100644 index 000000000..25b35aa55 --- /dev/null +++ b/client/src/javascript/components/sidebar/TransferRateGraph.tsx @@ -0,0 +1,274 @@ +import {area, curveMonotoneX, line} from 'd3-shape'; +import {max} from 'd3-array'; +import {observer} from 'mobx-react'; +import {reaction} from 'mobx'; +import {ScaleLinear, scaleLinear} from 'd3-scale'; +import {Selection, select} from 'd3-selection'; +import * as React from 'react'; + +import type {TransferDirection} from '@shared/types/TransferData'; + +import TransferDataStore, {TRANSFER_DIRECTIONS} from '../../stores/TransferDataStore'; + +export interface TransferRateGraphInspectorPoint { + uploadSpeed: number; + downloadSpeed: number; + nearestTimestamp: number; +} + +interface TransferRateGraphProps { + id: string; + height: number; + width: number; + onHover: (inspectorPoint: TransferRateGraphInspectorPoint) => void; + onMouseOut: () => void; +} + +@observer +class TransferRateGraph extends React.Component { + private static getGradient(slug: TransferDirection): React.ReactNode { + return ( + + + + + ); + } + + lastMouseX?: number; + xScale?: ScaleLinear; + yScale?: ScaleLinear; + graphRefs: { + graph: SVGSVGElement | null; + areDefined: boolean; + isHovered: boolean; + } & Record< + TransferDirection, + { + graphArea?: Selection; + inspectPoint?: Selection; + rateLine?: Selection; + } + > = { + graph: null, + areDefined: false, + isHovered: false, + download: {}, + upload: {}, + }; + + static defaultProps = { + width: 240, + }; + + constructor(props: TransferRateGraphProps) { + super(props); + + reaction( + () => TransferDataStore.transferRates, + () => { + this.handleTransferHistoryChange(); + }, + ); + } + + componentDidMount(): void { + this.renderGraphData(); + } + + componentDidUpdate(): void { + this.renderGraphData(); + } + + private setInspectorCoordinates(slug: TransferDirection, hoverPoint: number): number { + const { + graphRefs: { + [slug]: {inspectPoint}, + }, + xScale, + yScale, + } = this; + + if (xScale == null || yScale == null || inspectPoint == null) { + return 0; + } + + const historicalData = TransferDataStore.transferRates; + const upperSpeed = historicalData[slug][Math.ceil(hoverPoint)]; + const lowerSpeed = historicalData[slug][Math.floor(hoverPoint)]; + + const delta = upperSpeed - lowerSpeed; + const speedAtHoverPoint = lowerSpeed + delta * (hoverPoint % 1); + + const coordinates = {x: xScale(hoverPoint), y: yScale(speedAtHoverPoint)}; + + inspectPoint.attr('transform', `translate(${coordinates.x},${coordinates.y})`); + + return speedAtHoverPoint; + } + + handleMouseMove = (mouseX: number): void => { + this.lastMouseX = mouseX; + this.renderPrecisePointInspectors(); + }; + + handleMouseOut = (): void => { + const {graphRefs, props} = this; + + graphRefs.isHovered = false; + + TRANSFER_DIRECTIONS.forEach((direction: T) => { + const {inspectPoint} = graphRefs[direction]; + if (inspectPoint != null) { + graphRefs[direction].inspectPoint = inspectPoint.style('opacity', 0); + } + }); + + if (props.onMouseOut) { + props.onMouseOut(); + } + }; + + handleMouseOver = (): void => { + this.graphRefs.isHovered = true; + + TRANSFER_DIRECTIONS.forEach((direction: T) => { + const {inspectPoint} = this.graphRefs[direction]; + if (inspectPoint != null) { + this.graphRefs[direction].inspectPoint = inspectPoint.style('opacity', 1); + } + }); + }; + + handleTransferHistoryChange = (): void => { + this.updateGraph(); + }; + + private initGraph(): void { + if (this.graphRefs.areDefined === true) { + return; + } + + const {id} = this.props; + + const graph = select(`#${id}`); + TRANSFER_DIRECTIONS.forEach((direction: T) => { + // appendEmptyGraphShapes + this.graphRefs[direction].graphArea = graph + .append('path') + .attr('class', 'graph__area') + .attr('fill', `url('#graph__gradient--${direction}')`); + + // appendEmptyGraphLines + this.graphRefs[direction].rateLine = graph.append('path').attr('class', `graph__line graph__line--${direction}`); + + // appendGraphCircles + this.graphRefs[direction].inspectPoint = graph + .append('circle') + .attr('class', `graph__circle graph__circle--${direction}`) + .attr('r', 2.5); + }); + + this.graphRefs.areDefined = true; + } + + private updateGraph() { + this.renderGraphData(); + + if (this.graphRefs.isHovered) { + this.renderPrecisePointInspectors(); + } + } + + private renderPrecisePointInspectors(): void { + const { + lastMouseX, + props: {onHover}, + xScale, + } = this; + + if (xScale == null || lastMouseX == null) { + return; + } + + const historicalData = TransferDataStore.transferRates; + const hoverPoint = xScale.invert(lastMouseX); + const uploadSpeed = this.setInspectorCoordinates('upload', hoverPoint); + const downloadSpeed = this.setInspectorCoordinates('download', hoverPoint); + const nearestTimestamp = historicalData.timestamps[Math.round(hoverPoint)]; + + if (onHover) { + onHover({ + uploadSpeed, + downloadSpeed, + nearestTimestamp, + }); + } + } + + private renderGraphData(): void { + const historicalData = TransferDataStore.transferRates; + const {height, width} = this.props; + const margin = {bottom: 10, top: 10}; + + this.xScale = scaleLinear() + .domain([0, historicalData.download.length - 1]) + .range([0, width]); + + this.yScale = scaleLinear() + .domain([ + 0, + max(historicalData.download, (dataPoint, index) => Math.max(dataPoint, historicalData.upload[index])) as number, + ]) + .range([height - margin.top, margin.bottom]); + + this.initGraph(); + + const interpolation = curveMonotoneX; + TRANSFER_DIRECTIONS.forEach((direction: T) => { + const {xScale, yScale} = this; + const {graphArea, rateLine} = this.graphRefs[direction]; + + if (rateLine == null || graphArea == null || xScale == null || yScale == null) { + return; + } + + this.graphRefs[direction].rateLine = rateLine.attr( + 'd', + line() + .x((_dataPoint, index) => xScale(index) || 0) + .y((dataPoint) => yScale(dataPoint) || 0) + .curve(interpolation)(historicalData[direction]) as string, + ); + + this.graphRefs[direction].graphArea = graphArea.attr( + 'd', + area() + .x((_dataPoint, index) => xScale(index) || 0) + .y0(height) + .y1((dataPoint) => yScale(dataPoint) || 0) + .curve(interpolation)(historicalData[direction]) as string, + ); + }); + } + + render() { + const {id} = this.props; + + return ( + { + this.graphRefs.graph = ref; + }}> + + {TransferRateGraph.getGradient('upload')} + {TransferRateGraph.getGradient('download')} + + + ); + } +} + +export default TransferRateGraph; diff --git a/client/src/javascript/components/torrent-list/Action.js b/client/src/javascript/components/torrent-list/Action.js deleted file mode 100644 index 3652100d9..000000000 --- a/client/src/javascript/components/torrent-list/Action.js +++ /dev/null @@ -1,20 +0,0 @@ -import classnames from 'classnames'; -import React from 'react'; - -import Tooltip from '../general/Tooltip'; - -export default class Action extends React.Component { - render() { - const {clickHandler, icon, label, slug} = this.props; - const classes = classnames('action tooltip__wrapper', { - [`action--${slug}`]: slug != null, - }); - - return ( - - {icon} - {label} - - ); - } -} diff --git a/client/src/javascript/components/torrent-list/Action.tsx b/client/src/javascript/components/torrent-list/Action.tsx new file mode 100644 index 000000000..a28f5f1f6 --- /dev/null +++ b/client/src/javascript/components/torrent-list/Action.tsx @@ -0,0 +1,32 @@ +import classnames from 'classnames'; +import {FC, ReactNode} from 'react'; + +import Tooltip from '../general/Tooltip'; + +interface ActionProps { + clickHandler: () => void; + icon: ReactNode; + label: ReactNode; + slug: string; + noTip?: boolean; +} + +const Action: FC = (props: ActionProps) => { + const {clickHandler, icon, label, slug, noTip} = props; + const classes = classnames('action tooltip__wrapper', { + [`action--${slug}`]: slug != null, + }); + + return ( + + {icon} + {label} + + ); +}; + +Action.defaultProps = { + noTip: false, +}; + +export default Action; diff --git a/client/src/javascript/components/torrent-list/ActionBar.js b/client/src/javascript/components/torrent-list/ActionBar.js deleted file mode 100644 index ef0fae5e1..000000000 --- a/client/src/javascript/components/torrent-list/ActionBar.js +++ /dev/null @@ -1,118 +0,0 @@ -import classnames from 'classnames'; -import {injectIntl} from 'react-intl'; -import React from 'react'; - -import Action from './Action'; -import Add from '../icons/Add'; -import connectStores from '../../util/connectStores'; -import EventTypes from '../../constants/EventTypes'; -import Remove from '../icons/Remove'; -import SettingsStore from '../../stores/SettingsStore'; -import SortDropdown from './SortDropdown'; -import StartIcon from '../icons/StartIcon'; -import StopIcon from '../icons/StopIcon'; -import TorrentActions from '../../actions/TorrentActions'; -import TorrentStore from '../../stores/TorrentStore'; -import UIActions from '../../actions/UIActions'; - -class ActionBar extends React.Component { - handleAddTorrents() { - UIActions.displayModal({id: 'add-torrents'}); - } - - handleRemoveTorrents() { - UIActions.displayModal({ - id: 'remove-torrents', - }); - } - - handleSortChange(sortBy) { - SettingsStore.saveFloodSettings({id: 'sortTorrents', data: sortBy}); - UIActions.setTorrentsSort(sortBy); - } - - handleStart() { - TorrentActions.startTorrents(TorrentStore.getSelectedTorrents()); - } - - handleStop() { - TorrentActions.stopTorrents(TorrentStore.getSelectedTorrents()); - } - - render() { - const classes = classnames('action-bar', { - 'action-bar--is-condensed': this.props.torrentListViewSize === 'condensed', - }); - - return ( - - ); - } -} - -const ConnectedActionBar = connectStores(injectIntl(ActionBar), () => { - return [ - { - store: SettingsStore, - event: EventTypes.SETTINGS_CHANGE, - getValue: ({store}) => { - return { - sortBy: store.getFloodSettings('sortTorrents'), - torrentListViewSize: store.getFloodSettings('torrentListViewSize'), - }; - }, - }, - ]; -}); - -export default ConnectedActionBar; diff --git a/client/src/javascript/components/torrent-list/ActionBar.tsx b/client/src/javascript/components/torrent-list/ActionBar.tsx new file mode 100644 index 000000000..3f0ac8cf1 --- /dev/null +++ b/client/src/javascript/components/torrent-list/ActionBar.tsx @@ -0,0 +1,106 @@ +import classnames from 'classnames'; +import {FC} from 'react'; +import {observer} from 'mobx-react'; +import {useIntl} from 'react-intl'; + +import Action from './Action'; +import Add from '../icons/Add'; +import MenuIcon from '../icons/MenuIcon'; +import Remove from '../icons/Remove'; +import SettingActions from '../../actions/SettingActions'; +import SettingStore from '../../stores/SettingStore'; +import SortDropdown from './SortDropdown'; +import StartIcon from '../icons/StartIcon'; +import StopIcon from '../icons/StopIcon'; +import TorrentActions from '../../actions/TorrentActions'; +import TorrentStore from '../../stores/TorrentStore'; +import UIActions from '../../actions/UIActions'; + +const ActionBar: FC = observer(() => { + const intl = useIntl(); + const {sortTorrents: sortBy, torrentListViewSize} = SettingStore.floodSettings; + + const classes = classnames('action-bar', { + 'action-bar--is-condensed': torrentListViewSize === 'condensed', + }); + + return ( + + ); +}); + +export default ActionBar; diff --git a/client/src/javascript/components/torrent-list/SortDropdown.js b/client/src/javascript/components/torrent-list/SortDropdown.js deleted file mode 100644 index 6a4e311c5..000000000 --- a/client/src/javascript/components/torrent-list/SortDropdown.js +++ /dev/null @@ -1,102 +0,0 @@ -import {FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import Dropdown from '../general/form-elements/Dropdown'; -import TorrentProperties from '../../constants/TorrentProperties'; - -const METHODS_TO_BIND = ['getDropdownHeader', 'handleItemSelect']; -const SORT_PROPERTIES = [ - 'name', - 'eta', - 'downRate', - 'upRate', - 'ratio', - 'percentComplete', - 'downTotal', - 'upTotal', - 'sizeBytes', - 'dateAdded', -]; - -class SortDropdown extends React.Component { - constructor() { - super(); - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - getDropdownHeader() { - const {selectedProperty} = this.props; - let propertyMessageConfig = TorrentProperties[selectedProperty]; - - if (propertyMessageConfig == null) { - propertyMessageConfig = TorrentProperties.dateAdded; - } - - return ( - - ); - } - - getDropdownMenus() { - const {direction, selectedProperty} = this.props; - const items = SORT_PROPERTIES.map(sortProp => { - const isSelected = sortProp === selectedProperty; - const directionIndicator = isSelected ? ( - - ) : null; - - return { - displayName: ( -
    - {this.props.intl.formatMessage(TorrentProperties[sortProp])} - {directionIndicator} -
    - ), - selected: isSelected, - property: sortProp, - }; - }); - - // Dropdown expects an array of arrays. - return [items]; - } - - handleItemSelect(selection) { - let {direction} = this.props; - const {property} = selection; - - if (this.props.selectedProperty === property) { - direction = direction === 'asc' ? 'desc' : 'asc'; - } else { - direction = 'asc'; - } - - this.props.onSortChange({direction, property}); - } - - render() { - if (this.props.selectedProperty == null) { - return null; - } - - return ( - - ); - } -} - -export default injectIntl(SortDropdown); diff --git a/client/src/javascript/components/torrent-list/SortDropdown.tsx b/client/src/javascript/components/torrent-list/SortDropdown.tsx new file mode 100644 index 000000000..edc8890d5 --- /dev/null +++ b/client/src/javascript/components/torrent-list/SortDropdown.tsx @@ -0,0 +1,91 @@ +import {FC} from 'react'; +import {FormattedMessage, useIntl} from 'react-intl'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import Dropdown from '../general/form-elements/Dropdown'; +import TorrentListColumns from '../../constants/TorrentListColumns'; + +import type {TorrentListColumn} from '../../constants/TorrentListColumns'; + +const SORT_PROPERTIES = [ + 'name', + 'eta', + 'downRate', + 'upRate', + 'ratio', + 'percentComplete', + 'downTotal', + 'upTotal', + 'sizeBytes', + 'dateAdded', +] as const; + +interface SortDropdownProps { + selectedProperty: TorrentListColumn; + direction: 'asc' | 'desc'; + onSortChange: (sortBy: FloodSettings['sortTorrents']) => void; +} + +const SortDropdown: FC = (props: SortDropdownProps) => { + const {direction, selectedProperty, onSortChange} = props; + const intl = useIntl(); + + if (selectedProperty == null) { + return null; + } + + const header = ( + + ); + + const menuItems = [ + SORT_PROPERTIES.map((sortProp) => { + const isSelected = sortProp === selectedProperty; + const directionIndicator = isSelected ? ( + + ) : null; + + return { + displayName: ( +
    + {intl.formatMessage(TorrentListColumns[sortProp])} + {directionIndicator} +
    + ), + selected: isSelected, + property: sortProp, + }; + }), + ]; + + return ( + { + const {property} = selection; + + if (property == null) { + return; + } + + let nextDirection: 'asc' | 'desc' = 'asc'; + if (selectedProperty === property) { + nextDirection = direction === 'asc' ? 'desc' : 'asc'; + } + + onSortChange({direction: nextDirection, property}); + }} + header={header} + menuItems={menuItems} + /> + ); +}; + +export default SortDropdown; diff --git a/client/src/javascript/components/torrent-list/TableHeading.js b/client/src/javascript/components/torrent-list/TableHeading.js deleted file mode 100644 index 01414dc62..000000000 --- a/client/src/javascript/components/torrent-list/TableHeading.js +++ /dev/null @@ -1,173 +0,0 @@ -import classnames from 'classnames'; -import {FormattedMessage, injectIntl} from 'react-intl'; -import React from 'react'; - -import TorrentProperties from '../../constants/TorrentProperties'; -import UIStore from '../../stores/UIStore'; - -const methodsToBind = [ - 'getHeadingElements', - 'handleCellMouseDown', - 'handleMouseUp', - 'handleMouseMove', - 'updateCellWidth', -]; - -const mouseDownStyles = ` - body { user-select: none !important; } - * { cursor: col-resize !important; } -`; - -class TableHeading extends React.Component { - constructor() { - super(); - - this.focusedCell = null; - this.focusedCellWidth = null; - this.isMouseDown = false; - this.lastMouseX = null; - - methodsToBind.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - componentDidMount() { - this.tableHeadingX = this.tableHeading.getBoundingClientRect().left; - } - - handleMouseMove(event) { - let widthDelta = 0; - - if (this.lastMouseX != null) { - widthDelta = event.clientX - this.lastMouseX; - } - - const nextCellWidth = this.focusedCellWidth + widthDelta; - - if (nextCellWidth > 20) { - this.focusedCellWidth = nextCellWidth; - this.lastMouseX = event.clientX; - this.resizeLine.style.transform = `translateX(${Math.max( - 0, - event.clientX - this.tableHeadingX + this.props.scrollOffset, - )}px)`; - } - } - - handleMouseUp() { - UIStore.removeGlobalStyle(mouseDownStyles); - global.document.removeEventListener('mouseup', this.handleMouseUp); - global.document.removeEventListener('mousemove', this.handleMouseMove); - - this.isMouseDown = false; - this.lastMouseX = null; - this.resizeLine.style.opacity = 0; - - this.updateCellWidth(this.focusedCell, this.focusedCellWidth); - - this.focusedCell = null; - this.focusedCellWidth = null; - } - - handleCellClick(slug, event) { - this.props.onCellClick(slug, event); - } - - handleCellMouseDown(event, slug, width) { - if (!this.isMouseDown) { - UIStore.addGlobalStyle(mouseDownStyles); - global.document.addEventListener('mouseup', this.handleMouseUp); - global.document.addEventListener('mousemove', this.handleMouseMove); - - this.focusedCell = slug; - this.focusedCellWidth = width; - this.isMouseDown = true; - this.lastMouseX = event.clientX; - this.resizeLine.style.transform = `translateX(${Math.max( - 0, - event.clientX - this.tableHeadingX + this.props.scrollOffset, - )}px)`; - this.resizeLine.style.opacity = 1; - } - } - - updateCellWidth(cell, width) { - this.props.onWidthsChange({[cell]: width}); - } - - getHeadingElements() { - const {defaultWidth, defaultPropWidths, columns, propWidths, sortProp} = this.props; - - return columns.reduce((accumulator, {id, visible}) => { - if (!visible) { - return accumulator; - } - - let handle = null; - const width = propWidths[id] || defaultPropWidths[id] || defaultWidth; - - if (!this.isMouseDown) { - handle = ( - { - this.handleCellMouseDown(event, id, width); - }} - /> - ); - } - - const isSortActive = id === sortProp.property; - const classes = classnames('table__cell table__heading', { - 'table__heading--is-sorted': isSortActive, - [`table__heading--direction--${sortProp.direction}`]: isSortActive, - }); - - const label = ( - - ); - - accumulator.push( -
    this.handleCellClick(id, event)} - style={{width: `${width}px`}}> - - {label} - - {handle} -
    , - ); - - return accumulator; - }, []); - } - - render() { - return ( -
    { - this.tableHeading = ref; - }}> - {this.getHeadingElements()} -
    -
    { - this.resizeLine = ref; - }} - /> -
    - ); - } -} - -export default injectIntl(TableHeading); diff --git a/client/src/javascript/components/torrent-list/TableHeading.tsx b/client/src/javascript/components/torrent-list/TableHeading.tsx new file mode 100644 index 000000000..102592946 --- /dev/null +++ b/client/src/javascript/components/torrent-list/TableHeading.tsx @@ -0,0 +1,144 @@ +import classnames from 'classnames'; +import {forwardRef, MutableRefObject, ReactNodeArray, useRef, useState} from 'react'; +import {FormattedMessage, useIntl} from 'react-intl'; +import {observer} from 'mobx-react'; +import {useEnsuredForwardedRef} from 'react-use'; + +import TorrentListColumns, {TorrentListColumn} from '../../constants/TorrentListColumns'; +import SettingStore from '../../stores/SettingStore'; +import UIStore from '../../stores/UIStore'; + +const pointerDownStyles = ` + body { user-select: none !important; } + * { cursor: col-resize !important; } +`; + +interface TableHeadingProps { + onCellClick: (column: TorrentListColumn) => void; + onWidthsChange: (column: TorrentListColumn, width: number) => void; +} + +const TableHeading = observer( + forwardRef(({onCellClick, onWidthsChange}: TableHeadingProps, ref) => { + const [isPointerDown, setIsPointerDown] = useState(false); + + const focusedCell = useRef(); + const focusedCellWidth = useRef(); + const lastPointerX = useRef(); + const tableHeading = useEnsuredForwardedRef(ref as MutableRefObject); + const resizeLine = useRef(null); + + const intl = useIntl(); + + const handlePointerMove = (event: PointerEvent) => { + let widthDelta = 0; + if (lastPointerX.current != null) { + widthDelta = event.clientX - lastPointerX.current; + } + + let nextCellWidth = 20; + if (focusedCellWidth.current != null) { + nextCellWidth = focusedCellWidth.current + widthDelta; + } + + if (nextCellWidth > 20) { + focusedCellWidth.current = nextCellWidth; + lastPointerX.current = event.clientX; + if (resizeLine.current != null && tableHeading.current != null) { + resizeLine.current.style.transform = `translate(${Math.max(0, event.clientX)}px, ${ + tableHeading.current.getBoundingClientRect().top + }px)`; + } + } + }; + + const handlePointerUp = () => { + UIStore.removeGlobalStyle(pointerDownStyles); + window.removeEventListener('pointerup', handlePointerUp); + window.removeEventListener('pointermove', handlePointerMove); + + setIsPointerDown(false); + lastPointerX.current = undefined; + + if (resizeLine.current != null) { + resizeLine.current.style.opacity = '0'; + } + + if (focusedCell.current != null && focusedCellWidth.current != null) { + onWidthsChange(focusedCell.current, focusedCellWidth.current); + } + + focusedCell.current = undefined; + focusedCellWidth.current = undefined; + }; + + return ( +
    + {SettingStore.floodSettings.torrentListColumns.reduce((accumulator: ReactNodeArray, {id, visible}) => { + if (!visible) { + return accumulator; + } + + const labelID = TorrentListColumns[id]?.id; + if (labelID == null) { + return accumulator; + } + + let handle = null; + const width = SettingStore.floodSettings.torrentListColumnWidths[id] || 100; + + if (!isPointerDown) { + handle = ( + { + if (!isPointerDown && resizeLine.current != null && tableHeading.current != null) { + setIsPointerDown(true); + + focusedCell.current = id; + focusedCellWidth.current = width; + lastPointerX.current = event.clientX; + + window.addEventListener('pointerup', handlePointerUp); + window.addEventListener('pointermove', handlePointerMove); + UIStore.addGlobalStyle(pointerDownStyles); + + resizeLine.current.style.transform = `translate(${Math.max(0, event.clientX)}px, ${ + tableHeading.current.getBoundingClientRect().top + }px)`; + resizeLine.current.style.opacity = '1'; + } + }} + /> + ); + } + + const isSortActive = id === SettingStore.floodSettings.sortTorrents.property; + const classes = classnames('table__cell table__heading', { + 'table__heading--is-sorted': isSortActive, + [`table__heading--direction--${SettingStore.floodSettings.sortTorrents.direction}`]: isSortActive, + }); + + accumulator.push( +
    onCellClick(id)} style={{width: `${width}px`}}> + + + + {handle} +
    , + ); + + return accumulator; + }, [])} +
    +
    +
    + ); + }), +); + +export default TableHeading; diff --git a/client/src/javascript/components/torrent-list/Torrent.js b/client/src/javascript/components/torrent-list/Torrent.js deleted file mode 100644 index b5bb68781..000000000 --- a/client/src/javascript/components/torrent-list/Torrent.js +++ /dev/null @@ -1,264 +0,0 @@ -import React from 'react'; - -import CalendarIcon from '../icons/CalendarIcon'; -import ClockIcon from '../icons/ClockIcon'; -import DiskIcon from '../icons/DiskIcon'; -import DownloadThickIcon from '../icons/DownloadThickIcon'; -import InformationIcon from '../icons/InformationIcon'; -import PeersIcon from '../icons/PeersIcon'; -import ProgressBar from '../general/ProgressBar'; -import RatioIcon from '../icons/RatioIcon'; -import SeedsIcon from '../icons/SeedsIcon'; -import {torrentStatusIcons} from '../../util/torrentStatusIcons'; -import {torrentStatusClasses} from '../../util/torrentStatusClasses'; -import TorrentDetail from './TorrentDetail'; -import UploadThickIcon from '../icons/UploadThickIcon'; - -const condensedValueTransformers = { - downloadTotal: torrent => torrent.bytesDone, - peers: torrent => torrent.peersConnected, - percentComplete: torrent => ( - - ), - seeds: torrent => torrent.seedsConnected, -}; - -const condensedSecondaryValueTransformers = { - peers: torrent => torrent.peersTotal, - seeds: torrent => torrent.seedsTotal, -}; - -const expandedTorrentSectionContent = { - primary: ['name'], - secondary: ['eta', 'downRate', 'upRate'], - tertiary: ['*'], -}; - -const expandedTorrentDetailsToHide = ['downTotal']; - -const expandedValueTransformers = { - peers: torrent => torrent.peersConnected, - seeds: torrent => torrent.seedsConnected, -}; - -const expandedSecondaryValueTransformers = { - peers: torrent => torrent.peersTotal, - seeds: torrent => torrent.seedsTotal, - percentComplete: torrent => torrent.bytesDone, -}; - -const ICONS = { - clock: , - disk: , - downloadThick: , - information: , - calendar: , - peers: , - ratio: , - seeds: , - uploadThick: , -}; - -const METHODS_TO_BIND = ['handleClick', 'handleDoubleClick', 'handleRightClick']; - -const TORRENT_PRIMITIVES_TO_OBSERVE = ['bytesDone', 'downRate', 'peersTotal', 'seedsTotal', 'upRate']; - -const TORRENT_ARRAYS_TO_OBSERVE = ['status', 'tags']; - -class Torrent extends React.Component { - constructor() { - super(); - - METHODS_TO_BIND.forEach(method => { - this[method] = this[method].bind(this); - }); - } - - shouldComponentUpdate(nextProps) { - if (nextProps.isSelected !== this.props.isSelected || nextProps.isCondensed !== this.props.isCondensed) { - return true; - } - - const nextTorrent = nextProps.torrent; - const {torrent} = this.props; - - let shouldUpdate = TORRENT_ARRAYS_TO_OBSERVE.some(key => { - const nextArr = nextTorrent[key]; - const currentArr = this.props.torrent[key]; - - return ( - nextArr.length !== currentArr.length || nextArr.some((nextValue, index) => nextValue !== currentArr[index]) - ); - }); - - if (!shouldUpdate) { - shouldUpdate = TORRENT_PRIMITIVES_TO_OBSERVE.some(key => nextTorrent[key] !== torrent[key]); - } - - if (!shouldUpdate) { - shouldUpdate = Object.keys(nextProps.propWidths).some( - key => nextProps.propWidths[key] !== this.props.propWidths[key], - ); - } - - if (!shouldUpdate) { - shouldUpdate = nextProps.columns.some(({id}, index) => id !== this.props.columns[index].id); - } - - return shouldUpdate; - } - - getTags(tags) { - return tags.map(tag => ( -
  • - {tag} -
  • - )); - } - - getWidth(slug) { - const {defaultWidth, defaultPropWidths, propWidths} = this.props; - - return propWidths[slug] || defaultPropWidths[slug] || defaultWidth; - } - - handleClick(event) { - this.props.handleClick(this.props.torrent.hash, event); - } - - handleDoubleClick(event) { - this.props.handleDoubleClick(this.props.torrent, event); - } - - handleRightClick(event) { - if (!this.props.isSelected) { - this.handleClick(event); - } - - this.props.handleRightClick(this.props.torrent, event); - } - - render() { - const {isCondensed, isSelected, columns, torrent} = this.props; - const torrentClasses = torrentStatusClasses( - torrent, - { - 'torrent--is-selected': isSelected, - 'torrent--is-condensed': isCondensed, - 'torrent--is-expanded': !isCondensed, - }, - 'torrent', - ); - - if (isCondensed) { - const torrentPropertyColumns = columns.reduce((accumulator, {id, visible}) => { - if (!visible) { - return accumulator; - } - - let value = torrent[id]; - let secondaryValue; - - if (id in condensedValueTransformers) { - value = condensedValueTransformers[id](torrent); - } - - if (id in condensedSecondaryValueTransformers) { - secondaryValue = condensedSecondaryValueTransformers[id](torrent); - } - - accumulator.push( - , - ); - - return accumulator; - }, []); - - return ( -
  • - {torrentPropertyColumns} -
  • - ); - } - - const sections = {primary: [], secondary: [], tertiary: []}; - - // Using a for loop to maximize performance. - for (let index = 0; index < columns.length; index++) { - const {id, visible} = columns[index]; - - if (visible && !expandedTorrentDetailsToHide.includes(id)) { - let value = torrent[id]; - let secondaryValue; - - if (id in expandedValueTransformers) { - value = expandedValueTransformers[id](torrent); - } - - if (id in expandedSecondaryValueTransformers) { - secondaryValue = expandedSecondaryValueTransformers[id](torrent); - } - - if (expandedTorrentSectionContent.primary.includes(id)) { - sections.primary.push( - , - ); - } else if (expandedTorrentSectionContent.secondary.includes(id)) { - sections.secondary[expandedTorrentSectionContent.secondary.indexOf(id)] = ( - - ); - } else { - sections.tertiary.push( - , - ); - } - } - } - - return ( -
  • -
    - {sections.primary} -
    {sections.secondary}
    -
    -
    {sections.tertiary}
    -
    - -
    - -
  • - ); - } -} - -Torrent.defaultProps = { - isCondensed: false, -}; - -export default Torrent; diff --git a/client/src/javascript/components/torrent-list/TorrentDetail.js b/client/src/javascript/components/torrent-list/TorrentDetail.js deleted file mode 100644 index 1085d2566..000000000 --- a/client/src/javascript/components/torrent-list/TorrentDetail.js +++ /dev/null @@ -1,137 +0,0 @@ -import {FormattedDate, FormattedMessage, FormattedNumber} from 'react-intl'; -import React from 'react'; - -import CalendarCreatedIcon from '../icons/CalendarCreatedIcon'; -import CalendarIcon from '../icons/CalendarIcon'; -import Checkmark from '../icons/Checkmark'; -import ClockIcon from '../icons/ClockIcon'; -import CommentIcon from '../icons/CommentIcon'; -import DetailNotAvailableIcon from '../icons/DetailNotAvailableIcon'; -import DiskIcon from '../icons/DiskIcon'; -import DownloadThickIcon from '../icons/DownloadThickIcon'; -import Duration from '../general/Duration'; -import HashIcon from '../icons/HashIcon'; -import FolderClosedSolid from '../icons/FolderClosedSolid'; -import PeersIcon from '../icons/PeersIcon'; -import LockIcon from '../icons/LockIcon'; -import Ratio from '../general/Ratio'; -import RadarIcon from '../icons/RadarIcon'; -import RatioIcon from '../icons/RatioIcon'; -import SeedsIcon from '../icons/SeedsIcon'; -import Size from '../general/Size'; -import TrackerMessageIcon from '../icons/TrackerMessageIcon'; -import UploadThickIcon from '../icons/UploadThickIcon'; - -const icons = { - checkmark: , - comment: , - eta: , - sizeBytes: , - downRate: , - basePath: , - hash: , - dateAdded: , - dateCreated: , - isPrivate: , - message: , - percentComplete: , - peers: , - ratio: , - seeds: , - trackerURIs: , - upRate: , - upTotal: , -}; - -const booleanRenderer = value => (value ? icons.checkmark : null); -const dateRenderer = date => ; -const peersRenderer = (peersConnected, totalPeers) => ( - , - of: ( - - - - ), - total: , - }} - /> -); -const speedRenderer = value => ; -const sizeRenderer = value => ; - -const transformers = { - dateAdded: dateRenderer, - dateCreated: dateRenderer, - downRate: speedRenderer, - downTotal: sizeRenderer, - ignoreScheduler: booleanRenderer, - isPrivate: booleanRenderer, - percentComplete: (percent, size) => ( - - - % -  —  - - - ), - peers: peersRenderer, - seeds: peersRenderer, - tags: tags => ( -
      - {tags.map(tag => ( -
    • - {tag} -
    • - ))} -
    - ), - ratio: ratio => , - sizeBytes: sizeRenderer, - trackerURIs: trackers => trackers.join(', '), - upRate: speedRenderer, - upTotal: sizeRenderer, - eta: eta => { - if (!eta) { - return null; - } - - return ; - }, -}; - -class TorrentDetail extends React.PureComponent { - render() { - const {className, preventTransform, secondaryValue, slug, width} = this.props; - let {icon, value} = this.props; - - if (!preventTransform && slug in transformers) { - value = transformers[slug](value, secondaryValue); - } - - if (!value) { - value = ; - } - - if (icon) { - icon = icons[slug]; - } - - return ( -
    - {icon} - {value} -
    - ); - } -} - -TorrentDetail.defaultProps = { - preventTransform: false, - className: '', -}; - -export default TorrentDetail; diff --git a/client/src/javascript/components/torrent-list/TorrentList.js b/client/src/javascript/components/torrent-list/TorrentList.js deleted file mode 100644 index 42669253d..000000000 --- a/client/src/javascript/components/torrent-list/TorrentList.js +++ /dev/null @@ -1,571 +0,0 @@ -import {Button} from 'flood-ui-kit'; -import {FormattedMessage, injectIntl} from 'react-intl'; -import _ from 'lodash'; -import Dropzone from 'react-dropzone'; -import React from 'react'; - -import ClientStatusStore from '../../stores/ClientStatusStore'; -import ConfigStore from '../../stores/ConfigStore'; -import connectStores from '../../util/connectStores'; -import CustomScrollbars from '../general/CustomScrollbars'; -import EventTypes from '../../constants/EventTypes'; -import Files from '../icons/Files'; -import GlobalContextMenuMountPoint from '../general/GlobalContextMenuMountPoint'; -import ListViewport from '../general/ListViewport'; -import PriorityMeter from '../general/filesystem/PriorityMeter'; -import SettingsStore from '../../stores/SettingsStore'; -import TableHeading from './TableHeading'; -import Torrent from './Torrent'; -import TorrentActions from '../../actions/TorrentActions'; -import TorrentFilterStore from '../../stores/TorrentFilterStore'; -import TorrentStore from '../../stores/TorrentStore'; -import UIActions from '../../actions/UIActions'; - -const defaultWidth = 100; -const defaultPropWidths = { - name: 200, - eta: 100, -}; - -class TorrentListContainer extends React.Component { - handleTorrentPriorityChange = null; - - lastScrollLeft = 0; - - state = { - tableScrollLeft: 0, - torrentListViewportSize: null, - }; - - componentDidMount() { - TorrentStore.listen(EventTypes.UI_TORRENT_SELECTION_CHANGE, this.handleTorrentSelectionChange); - TorrentFilterStore.listen(EventTypes.UI_TORRENTS_FILTER_CHANGE, this.handleTorrentFilterChange); - global.addEventListener('resize', this.updateTorrentListViewWidth); - } - - componentDidUpdate(prevProps) { - const {torrentListViewSize: currentTorrentListViewSize} = this.props; - const isCondensed = currentTorrentListViewSize === 'condensed'; - const wasCondensed = prevProps.torrentListViewSize === 'condensed'; - - if (this.horizontalScrollRef != null && this.state.torrentListViewportSize == null) { - this.updateTorrentListViewWidth(); - } - - if (this.verticalScrollbarThumb != null) { - if (!isCondensed && wasCondensed) { - this.updateVerticalThumbPosition(0); - } else if (isCondensed) { - this.updateVerticalThumbPosition( - (this.getTotalCellWidth() - this.listContainer.clientWidth) * -1 + this.lastScrollLeft, - ); - } - } - - if (currentTorrentListViewSize !== prevProps.torrentListViewSize && this.listViewportRef != null) { - this.listViewportRef.measureItemHeight(); - } - } - - componentWillUnmount() { - TorrentStore.unlisten(EventTypes.UI_TORRENT_SELECTION_CHANGE, this.handleTorrentSelectionChange); - TorrentFilterStore.unlisten(EventTypes.UI_TORRENTS_FILTER_CHANGE, this.handleTorrentFilterChange); - global.removeEventListener('resize', this.updateTorrentListViewWidth); - } - - handleClearFiltersClick() { - TorrentFilterStore.clearAllFilters(); - } - - getContextMenuItems(torrent) { - const {intl} = this.props; - const clickHandler = this.handleContextMenuItemClick; - - return [ - { - action: 'start', - clickHandler, - label: intl.formatMessage({ - id: 'torrents.list.context.start', - defaultMessage: 'Start', - }), - }, - { - action: 'stop', - clickHandler, - label: intl.formatMessage({ - id: 'torrents.list.context.stop', - defaultMessage: 'Stop', - }), - }, - { - action: 'remove', - clickHandler, - label: intl.formatMessage({ - id: 'torrents.list.context.remove', - defaultMessage: 'Remove', - }), - }, - { - action: 'check-hash', - clickHandler, - label: intl.formatMessage({ - id: 'torrents.list.context.check.hash', - defaultMessage: 'Check Hash', - }), - }, - { - type: 'separator', - }, - { - action: 'set-taxonomy', - clickHandler, - label: intl.formatMessage({ - id: 'torrents.list.context.set.tags', - defaultMessage: 'Set Tags', - }), - }, - { - action: 'move', - clickHandler, - label: intl.formatMessage({ - id: 'torrents.list.context.move', - defaultMessage: 'Set Torrent Location', - }), - }, - { - type: 'separator', - }, - { - action: 'torrent-details', - clickHandler: (action, event) => { - clickHandler(action, event, torrent); - }, - label: intl.formatMessage({ - id: 'torrents.list.context.details', - defaultMessage: 'Torrent Details', - }), - }, - { - action: 'torrent-download-tar', - clickHandler: (action, event) => { - clickHandler(action, event, torrent); - }, - label: intl.formatMessage({ - id: 'torrents.list.context.download', - defaultMessage: 'Download', - }), - }, - { - action: 'set-priority', - clickHandler, - dismissMenu: false, - label: intl.formatMessage({ - id: 'torrents.list.context.priority', - defaultMessage: 'Priority', - }), - labelAction: ( - - ), - }, - ]; - } - - handleContextMenuItemClick = (action, event, torrent) => { - const selectedTorrents = TorrentStore.getSelectedTorrents(); - switch (action) { - case 'check-hash': - TorrentActions.checkHash(selectedTorrents); - break; - case 'set-taxonomy': - UIActions.displayModal({id: 'set-taxonomy'}); - break; - case 'start': - TorrentActions.startTorrents(selectedTorrents); - break; - case 'stop': - TorrentActions.stopTorrents(selectedTorrents); - break; - case 'remove': - UIActions.displayModal({id: 'remove-torrents'}); - break; - case 'move': - UIActions.displayModal({id: 'move-torrents'}); - break; - case 'torrent-details': - this.handleDetailsClick(torrent, event); - break; - case 'torrent-download-tar': - this.handleTorrentDownload(torrent, event); - break; - case 'set-priority': - this.handleTorrentPriorityChange(event); - break; - default: - break; - } - }; - - handleDetailsClick = (torrent, event) => { - UIActions.handleDetailsClick({ - hash: torrent.hash, - event, - }); - - UIActions.displayModal({ - id: 'torrent-details', - options: {hash: torrent.hash}, - }); - }; - - handleTorrentDownload(torrent, event) { - event.preventDefault(); - const baseURI = ConfigStore.getBaseURI(); - const link = document.createElement('a'); - link.download = torrent.isMultiFile ? `${torrent.name}.tar` : torrent.name; - link.href = `${baseURI}api/download?hash=${torrent.hash}`; - link.style.display = 'none'; - document.body.appendChild(link); - link.click(); - } - - handleDoubleClick(torrent, event) { - this.handleDetailsClick(torrent, event); - } - - handleContextMenuClick = (torrent, event) => { - event.preventDefault(); - - UIActions.displayContextMenu({ - id: 'torrent-list-item', - clickPosition: { - x: event.clientX, - y: event.clientY, - }, - items: this.getContextMenuItems(torrent), - }); - }; - - handleFileDrop = files => { - const destination = - SettingsStore.getFloodSettings('torrentDestination') || SettingsStore.getClientSettings('directoryDefault') || ''; - - const isBasePath = false; - - const start = SettingsStore.getFloodSettings('startTorrentsOnLoad'); - - const fileData = new FormData(); - - files.forEach(file => { - fileData.append('torrents', file); - }); - - fileData.append('destination', destination); - fileData.append('isBasePath', isBasePath); - fileData.append('start', start); - fileData.append('tags', ''); - - TorrentActions.addTorrentsByFiles(fileData, destination); - }; - - handleTorrentFilterChange = () => { - if (this.listViewportRef != null) { - this.listViewportRef.scrollToTop(); - } - }; - - handleTorrentSelectionChange = () => { - this.forceUpdate(); - }; - - getEmptyTorrentListNotification() { - let clearFilters = null; - - if (TorrentFilterStore.isFilterActive()) { - clearFilters = ( -
    - -
    - ); - } - - return ( -
    -
    - -
    - {clearFilters} -
    - ); - } - - getCellWidth(slug) { - const value = this.props.torrentListColumnWidths[slug] || defaultPropWidths[slug] || defaultWidth; - - return value; - } - - getListWrapperStyle(options = {}) { - if (options.isCondensed && !options.isListEmpty) { - const totalCellWidth = this.getTotalCellWidth(); - - if (totalCellWidth >= this.state.torrentListViewportSize) { - return {width: `${totalCellWidth}px`}; - } - } - - return null; - } - - getTotalCellWidth() { - return this.props.displayedProperties.reduce((accumulator, {id, visible}) => { - if (!visible) { - return accumulator; - } - - return accumulator + this.getCellWidth(id); - }, 0); - } - - getVerticalScrollbarThumb = (props, onMouseUp) => { - return ( -
    -
    { - this.verticalScrollbarThumb = ref; - }} - role="button" - tabIndex={0} - /> -
    - ); - }; - - bindExternalPriorityChangeHandler = priorityChangeHandler => { - this.handleTorrentPriorityChange = priorityChangeHandler; - }; - - handleTableHeadingCellClick(slug) { - const currentSort = TorrentFilterStore.getTorrentsSort(); - - let nextDirection = 'asc'; - - if (currentSort.property === slug) { - nextDirection = currentSort.direction === 'asc' ? 'desc' : 'asc'; - } - - const sortBy = { - property: slug, - direction: nextDirection, - }; - - SettingsStore.saveFloodSettings({id: 'sortTorrents', data: sortBy}); - UIActions.setTorrentsSort(sortBy); - } - - handleTorrentClick(hash, event) { - UIActions.handleTorrentClick({hash, event}); - } - - handleTorrentPriorityChange(hash, level) { - TorrentActions.setPriority(hash, level); - } - - handleHorizontalScroll = event => { - if (this.verticalScrollbarThumb != null) { - const {clientWidth, scrollLeft, scrollWidth} = event.target; - this.lastScrollLeft = scrollLeft; - this.updateVerticalThumbPosition((scrollWidth - clientWidth) * -1 + scrollLeft); - } - }; - - handleHorizontalScrollStop = () => { - this.setState({tableScrollLeft: this.lastScrollLeft}); - }; - - handlePropWidthChange = newPropWidths => { - SettingsStore.saveFloodSettings({ - id: 'torrentListColumnWidths', - data: {...this.props.torrentListColumnWidths, ...newPropWidths}, - }); - }; - - /* eslint-disable react/sort-comp */ - updateTorrentListViewWidth = _.debounce( - () => { - if (this.horizontalScrollRef != null) { - this.setState({ - torrentListViewportSize: this.horizontalScrollRef.scrollbarRef.getClientWidth(), - }); - } - }, - 100, - {trailing: true}, - ); - /* eslint-enable react/sort-comp */ - - updateVerticalThumbPosition = offset => { - this.verticalScrollbarThumb.style.transform = `translateX(${offset}px)`; - }; - - renderListItem = index => { - const selectedTorrents = TorrentStore.getSelectedTorrents(); - const {displayedProperties, torrentListViewSize, torrentListColumnWidths, torrents} = this.props; - const torrent = torrents[index]; - const {hash} = torrent; - - return ( - - ); - }; - - render() { - const {displayedProperties, torrentListColumnWidths, isClientConnected, torrentListViewSize, torrents} = this.props; - let content = null; - let torrentListHeading = null; - const isCondensed = torrentListViewSize === 'condensed'; - const isListEmpty = torrents.length === 0; - const listWrapperStyle = this.getListWrapperStyle({ - isCondensed, - isListEmpty, - }); - - if (!isClientConnected) { - content = ( -
    -
    - -
    -
    - ); - } else if (isListEmpty) { - content = this.getEmptyTorrentListNotification(); - } else { - content = ( - { - this.listViewportRef = ref; - }} - scrollContainerClass="torrent__list__scrollbars--vertical" - /> - ); - - if (isCondensed) { - torrentListHeading = ( - - ); - } - } - - return ( - { - this.listContainer = ref; - }} - onDrop={this.handleFileDrop} - disableClick - disablePreview> - { - this.horizontalScrollRef = ref; - }}> -
    - - {torrentListHeading} - {content} -
    -
    - -
    -
    -
    - -
    - -
    -
    -
    - ); - } -} - -const ConnectedActionBar = connectStores(injectIntl(TorrentListContainer), () => { - return [ - { - store: ClientStatusStore, - event: EventTypes.CLIENT_CONNECTION_STATUS_CHANGE, - getValue: ({store}) => { - return { - isClientConnected: store.getIsConnected(), - }; - }, - }, - { - store: SettingsStore, - event: EventTypes.SETTINGS_CHANGE, - getValue: ({store}) => { - return { - displayedProperties: store.getFloodSettings('torrentDetails'), - torrentListColumnWidths: store.getFloodSettings('torrentListColumnWidths'), - torrentListViewSize: store.getFloodSettings('torrentListViewSize'), - }; - }, - }, - { - store: TorrentStore, - event: [EventTypes.UI_TORRENTS_LIST_FILTERED, EventTypes.CLIENT_TORRENTS_REQUEST_SUCCESS], - getValue: ({store}) => { - return { - torrents: store.getTorrents(), - }; - }, - }, - ]; -}); - -export default ConnectedActionBar; diff --git a/client/src/javascript/components/torrent-list/TorrentList.tsx b/client/src/javascript/components/torrent-list/TorrentList.tsx new file mode 100644 index 000000000..798b122b8 --- /dev/null +++ b/client/src/javascript/components/torrent-list/TorrentList.tsx @@ -0,0 +1,227 @@ +import {Component, createRef, FC, ReactNode} from 'react'; +import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; +import {observer} from 'mobx-react'; +import {observable, reaction} from 'mobx'; +import {useDropzone} from 'react-dropzone'; + +import type {FixedSizeList} from 'react-window'; + +import defaultFloodSettings from '@shared/constants/defaultFloodSettings'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import {Button} from '../../ui'; +import ClientStatusStore from '../../stores/ClientStatusStore'; +import ContextMenuMountPoint from '../general/ContextMenuMountPoint'; +import Files from '../icons/Files'; +import ListViewport from '../general/ListViewport'; +import SettingActions from '../../actions/SettingActions'; +import SettingStore from '../../stores/SettingStore'; +import TableHeading from './TableHeading'; +import TorrentActions from '../../actions/TorrentActions'; +import TorrentFilterStore from '../../stores/TorrentFilterStore'; +import TorrentListRow from './TorrentListRow'; +import TorrentStore from '../../stores/TorrentStore'; + +import type {TorrentListColumn} from '../../constants/TorrentListColumns'; + +const TorrentDropzone: FC<{children: ReactNode}> = ({children}: {children: ReactNode}) => { + const handleFileDrop = (files: Array) => { + const filesData: Array = []; + + const callback = (data: string) => { + filesData.push(data); + + if (filesData.length === files.length && filesData[0] != null) { + TorrentActions.addTorrentsByFiles({ + files: filesData as [string, ...string[]], + destination: + SettingStore.floodSettings.torrentDestinations?.[''] ?? SettingStore.clientSettings?.directoryDefault ?? '', + isBasePath: false, + start: SettingStore.floodSettings.startTorrentsOnLoad, + }); + } + }; + + files.forEach((file) => { + const reader = new FileReader(); + reader.onload = (e) => { + if (e.target?.result != null && typeof e.target.result === 'string') { + callback(e.target.result.split('base64,')[1]); + } + }; + reader.readAsDataURL(file); + }); + }; + const {getRootProps, isDragActive} = useDropzone({ + onDrop: handleFileDrop, + noClick: true, + noKeyboard: true, + }); + + return ( +
    evt.preventDefault()})} + className={`dropzone dropzone--with-overlay torrents ${isDragActive ? 'dropzone--is-dragging' : ''}`}> + {children} +
    + ); +}; + +const getEmptyTorrentListNotification = (): ReactNode => { + let clearFilters = null; + + if (TorrentFilterStore.isFilterActive) { + clearFilters = ( +
    + +
    + ); + } + + return ( +
    +
    + +
    + {clearFilters} +
    + ); +}; + +@observer +class TorrentList extends Component { + listHeaderRef = createRef(); + listViewportRef = createRef(); + + torrentListViewportSize = observable.object<{ + width: number; + height: number; + }>({ + width: window.innerWidth, + height: window.innerHeight, + }); + + constructor(props: WrappedComponentProps) { + super(props); + + reaction(() => TorrentFilterStore.filters, this.handleTorrentFilterChange); + } + + handleColumnWidthChange = (column: TorrentListColumn, width: number) => { + const {torrentListColumnWidths = defaultFloodSettings.torrentListColumnWidths} = SettingStore.floodSettings; + + SettingActions.saveSetting('torrentListColumnWidths', { + ...torrentListColumnWidths, + [column]: width, + }); + }; + + handleTorrentFilterChange = () => { + if (this.listViewportRef.current != null) { + this.listViewportRef.current.scrollTo(0); + } + }; + + handleViewportScroll = (scrollLeft: number) => { + if (this.listHeaderRef.current != null) { + this.listHeaderRef.current.scrollLeft = scrollLeft; + } + }; + + render() { + const torrents = TorrentStore.filteredTorrents; + const {torrentListViewSize = 'condensed'} = SettingStore.floodSettings; + + const isCondensed = torrentListViewSize === 'condensed'; + const isListEmpty = torrents == null || torrents.length === 0; + + let content: ReactNode = null; + let torrentListHeading: ReactNode = null; + if (!ClientStatusStore.isConnected) { + content = ( +
    +
    + +
    +
    + ); + } else if (isListEmpty || torrents == null) { + content = getEmptyTorrentListNotification(); + } else { + if (isCondensed) { + torrentListHeading = ( + { + const currentSort = SettingStore.floodSettings.sortTorrents; + + let nextDirection: FloodSettings['sortTorrents']['direction'] = 'asc'; + + if (currentSort.property === property) { + nextDirection = currentSort.direction === 'asc' ? 'desc' : 'asc'; + } + + const sortBy = { + property, + direction: nextDirection, + }; + + SettingActions.saveSetting('sortTorrents', sortBy); + }} + onWidthsChange={this.handleColumnWidthChange} + ref={this.listHeaderRef} + /> + ); + } + + // itemSize must sync with styles &--is-condensed and &--is-expanded + content = ( + { + const {hash} = TorrentStore.filteredTorrents[index]; + + return ; + }} + itemSize={isCondensed ? 30 : 70} + listLength={torrents.length} + ref={this.listViewportRef} + outerRef={(ref) => { + const viewportDiv = ref; + if (viewportDiv != null && viewportDiv.onscroll == null) { + viewportDiv.onscroll = () => { + this.handleViewportScroll(viewportDiv.scrollLeft); + }; + } + }} + /> + ); + } + + return ( + +
    + + {torrentListHeading} + {content} +
    +
    +
    +
    + +
    + +
    +
    +
    + ); + } +} + +export default injectIntl(TorrentList); diff --git a/client/src/javascript/components/torrent-list/TorrentListCell.tsx b/client/src/javascript/components/torrent-list/TorrentListCell.tsx new file mode 100644 index 000000000..65c3df0a6 --- /dev/null +++ b/client/src/javascript/components/torrent-list/TorrentListCell.tsx @@ -0,0 +1,49 @@ +import classnames from 'classnames'; +import {FC, ReactNode} from 'react'; +import {observer} from 'mobx-react'; + +import type {TorrentProperties} from '@shared/types/Torrent'; + +import DetailNotAvailableIcon from '../icons/DetailNotAvailableIcon'; +import {getTorrentListCellContent} from '../../util/torrentListCellContents'; +import torrentPropertyIcons from '../../util/torrentPropertyIcons'; +import TorrentStore from '../../stores/TorrentStore'; + +import type {TorrentListColumn} from '../../constants/TorrentListColumns'; + +interface TorrentListCellProps { + hash: string; + column: TorrentListColumn; + content?: (torrent: TorrentProperties, column: TorrentListColumn) => ReactNode; + className?: string; + classNameOverride?: boolean; + width?: number; + showIcon?: boolean; +} + +const TorrentListCell: FC = observer( + ({hash, content, column, className, classNameOverride, width, showIcon}: TorrentListCellProps) => { + const icon = showIcon ? torrentPropertyIcons[column] : null; + + return ( +
    + {icon} + {content?.(TorrentStore.torrents[hash], column) || } +
    + ); + }, +); + +TorrentListCell.defaultProps = { + className: undefined, + classNameOverride: false, + content: getTorrentListCellContent, + width: undefined, + showIcon: false, +}; + +export default TorrentListCell; diff --git a/client/src/javascript/components/torrent-list/TorrentListContextMenu.tsx b/client/src/javascript/components/torrent-list/TorrentListContextMenu.tsx new file mode 100644 index 000000000..583644aca --- /dev/null +++ b/client/src/javascript/components/torrent-list/TorrentListContextMenu.tsx @@ -0,0 +1,161 @@ +import {createRef, MutableRefObject} from 'react'; + +import type {TorrentProperties} from '@shared/types/Torrent'; + +import ConfigStore from '../../stores/ConfigStore'; +import PriorityMeter from '../general/PriorityMeter'; +import TorrentActions from '../../actions/TorrentActions'; +import TorrentContextMenuActions from '../../constants/TorrentContextMenuActions'; +import TorrentStore from '../../stores/TorrentStore'; +import UIActions from '../../actions/UIActions'; + +import type {ContextMenuItem} from '../../stores/UIStore'; + +const handleTorrentDownload = (hash: TorrentProperties['hash']): void => { + const {baseURI} = ConfigStore; + const link = document.createElement('a'); + + link.download = ''; + link.href = `${baseURI}api/torrents/${hash}/contents/all/data`; + link.style.display = 'none'; + + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +}; + +const getContextMenuItems = (torrent: TorrentProperties): Array => { + const changePriorityFuncRef = createRef<() => number>(); + + return [ + { + type: 'action', + action: 'start', + label: TorrentContextMenuActions.start.id, + clickHandler: () => { + TorrentActions.startTorrents({ + hashes: TorrentStore.selectedTorrents, + }); + }, + }, + { + type: 'action', + action: 'stop', + label: TorrentContextMenuActions.stop.id, + clickHandler: () => { + TorrentActions.stopTorrents({ + hashes: TorrentStore.selectedTorrents, + }); + }, + }, + { + type: 'action', + action: 'remove', + label: TorrentContextMenuActions.remove.id, + clickHandler: () => { + UIActions.displayModal({id: 'remove-torrents'}); + }, + }, + { + type: 'action', + action: 'checkHash', + label: TorrentContextMenuActions.checkHash.id, + clickHandler: () => { + TorrentActions.checkHash({ + hashes: TorrentStore.selectedTorrents, + }); + }, + }, + { + type: 'separator', + }, + { + type: 'action', + action: 'setTaxonomy', + label: TorrentContextMenuActions.setTaxonomy.id, + clickHandler: () => { + UIActions.displayModal({id: 'set-taxonomy'}); + }, + }, + { + type: 'action', + action: 'move', + label: TorrentContextMenuActions.move.id, + clickHandler: () => { + UIActions.displayModal({id: 'move-torrents'}); + }, + }, + { + type: 'action', + action: 'setTrackers', + label: TorrentContextMenuActions.setTrackers.id, + clickHandler: () => { + UIActions.displayModal({id: 'set-trackers'}); + }, + }, + { + type: 'separator', + }, + { + type: 'action', + action: 'torrentDetails', + label: TorrentContextMenuActions.torrentDetails.id, + clickHandler: () => { + const {selectedTorrents} = TorrentStore; + UIActions.displayModal({ + id: 'torrent-details', + hash: selectedTorrents[selectedTorrents.length - 1], + }); + }, + }, + { + type: 'action', + action: 'torrentDownload', + label: TorrentContextMenuActions.torrentDownload.id, + clickHandler: (e) => { + const {selectedTorrents} = TorrentStore; + e.preventDefault(); + handleTorrentDownload(selectedTorrents[selectedTorrents.length - 1]); + }, + }, + { + type: 'action', + action: 'generateMagnet', + label: TorrentContextMenuActions.generateMagnet.id, + clickHandler: () => { + UIActions.displayModal({id: 'generate-magnet'}); + }, + }, + { + type: 'action', + action: 'setPriority', + label: TorrentContextMenuActions.setPriority.id, + clickHandler: () => { + if (changePriorityFuncRef.current != null) { + TorrentActions.setPriority({ + hashes: TorrentStore.selectedTorrents, + priority: changePriorityFuncRef.current(), + }); + } + }, + dismissMenu: false, + labelAction: () => ( + undefined} + priorityType="torrent" + showLabel={false} + changePriorityFuncRef={changePriorityFuncRef as MutableRefObject<() => number>} + clickHandled + /> + ), + }, + ]; +}; + +export default { + getContextMenuItems, +}; diff --git a/client/src/javascript/components/torrent-list/TorrentListRow.tsx b/client/src/javascript/components/torrent-list/TorrentListRow.tsx new file mode 100644 index 000000000..80e5313f0 --- /dev/null +++ b/client/src/javascript/components/torrent-list/TorrentListRow.tsx @@ -0,0 +1,133 @@ +import classnames from 'classnames'; +import {CSSProperties, FC, MouseEvent, TouchEvent, useRef, useState} from 'react'; +import {observer} from 'mobx-react'; +import {useLongPress} from 'react-use'; + +import defaultFloodSettings from '@shared/constants/defaultFloodSettings'; + +import SettingStore from '../../stores/SettingStore'; +import TorrentListContextMenu from './TorrentListContextMenu'; +import TorrentListRowCondensed from './TorrentListRowCondensed'; +import TorrentListRowExpanded from './TorrentListRowExpanded'; +import torrentStatusClasses from '../../util/torrentStatusClasses'; +import TorrentStore from '../../stores/TorrentStore'; +import UIActions from '../../actions/UIActions'; + +const displayContextMenu = (hash: string, event: MouseEvent | TouchEvent) => { + if (event.cancelable === true) { + event.preventDefault(); + } + + const mouseClientX = ((event as unknown) as MouseEvent).clientX; + const mouseClientY = ((event as unknown) as MouseEvent).clientY; + const touchClientX = ((event as unknown) as TouchEvent).touches?.[0].clientX; + const touchClientY = ((event as unknown) as TouchEvent).touches?.[0].clientY; + + if (!TorrentStore.selectedTorrents.includes(hash)) { + UIActions.handleTorrentClick({hash, event}); + } + + const {torrentContextMenuActions = defaultFloodSettings.torrentContextMenuActions} = SettingStore.floodSettings; + const torrent = TorrentStore.torrents[hash]; + + UIActions.displayContextMenu({ + id: 'torrent-list-item', + clickPosition: { + x: mouseClientX || touchClientX || 0, + y: mouseClientY || touchClientY || 0, + }, + items: TorrentListContextMenu.getContextMenuItems(torrent).filter((item) => { + if (item.type === 'separator') { + return true; + } + + return !torrentContextMenuActions.some((action) => action.id === item.action && action.visible === false); + }), + }); +}; + +const displayTorrentDetails = (hash: string) => UIActions.displayModal({id: 'torrent-details', hash}); + +const selectTorrent = (hash: string, event: MouseEvent | TouchEvent) => UIActions.handleTorrentClick({hash, event}); + +interface TorrentListRowProps { + hash: string; + style: CSSProperties; +} + +const TorrentListRow: FC = observer(({hash, style}: TorrentListRowProps) => { + const [rowLocation, setRowLocation] = useState(0); + const shouldDisplayTorrentDetails = useRef(false); + const rowRef = useRef(null); + + const isCondensed = SettingStore.floodSettings.torrentListViewSize === 'condensed'; + + const {status, upRate, downRate} = TorrentStore.torrents?.[hash] || {}; + const torrentClasses = torrentStatusClasses( + {status, upRate, downRate}, + classnames({ + 'torrent--is-selected': TorrentStore.selectedTorrents.includes(hash), + 'torrent--is-condensed': isCondensed, + 'torrent--is-expanded': !isCondensed, + }), + 'torrent', + ); + + const {onTouchStart, onTouchEnd} = useLongPress((e) => { + const curRowLocation = rowRef.current?.getBoundingClientRect().top; + if (e != null && curRowLocation != null && Math.abs(curRowLocation - rowLocation) < 25) { + displayContextMenu(hash, (e as unknown) as TouchEvent); + } + }); + + const onTouchStartHooked = (e: TouchEvent) => { + if (!TorrentStore.selectedTorrents.includes(hash)) { + selectTorrent(hash, e); + } + + if (shouldDisplayTorrentDetails.current) { + displayTorrentDetails(hash); + } else { + shouldDisplayTorrentDetails.current = true; + setTimeout(() => { + shouldDisplayTorrentDetails.current = false; + }, 200); + } + + setRowLocation(rowRef.current?.getBoundingClientRect().top || 0); + + onTouchStart(e); + }; + + if (isCondensed) { + return ( + + ); + } + + return ( + + ); +}); + +export default TorrentListRow; diff --git a/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx b/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx new file mode 100644 index 000000000..46b1fe8bf --- /dev/null +++ b/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx @@ -0,0 +1,95 @@ +import {forwardRef} from 'react'; +import {observer} from 'mobx-react'; + +import ProgressBar from '../general/ProgressBar'; +import SettingStore from '../../stores/SettingStore'; +import TorrentListCell from './TorrentListCell'; +import TorrentListColumns from '../../constants/TorrentListColumns'; +import torrentStatusIcons from '../../util/torrentStatusIcons'; + +interface TorrentListRowCondensedProps { + className: string; + style: React.CSSProperties; + hash: string; + handleClick: (hash: string, event: React.MouseEvent) => void; + handleDoubleClick: (hash: string, event: React.MouseEvent) => void; + handleRightClick: (hash: string, event: React.MouseEvent) => void; + handleTouchStart: (event: React.TouchEvent) => void; + handleTouchEnd: (event: React.TouchEvent) => void; +} + +const TorrentListRowCondensed = observer( + forwardRef( + ( + { + className, + style, + hash, + handleClick, + handleDoubleClick, + handleRightClick, + handleTouchStart, + handleTouchEnd, + }: TorrentListRowCondensedProps, + ref, + ) => { + const torrentListColumns = SettingStore.floodSettings.torrentListColumns.reduce( + (accumulator: React.ReactNodeArray, {id, visible}) => { + if (TorrentListColumns[id] == null) { + return accumulator; + } + + if (!visible) { + return accumulator; + } + + if (id === 'percentComplete') { + accumulator.push( + ( + + )} + width={SettingStore.floodSettings.torrentListColumnWidths[id]} + />, + ); + + return accumulator; + } + + accumulator.push( + , + ); + + return accumulator; + }, + [], + ); + + return ( +
  • handleClick(hash, e)} + onContextMenu={(e) => handleRightClick(hash, e)} + onDoubleClick={(e) => handleDoubleClick(hash, e)} + onTouchStart={handleTouchStart} + onTouchEnd={handleTouchEnd} + ref={ref}> + {torrentListColumns} +
  • + ); + }, + ), +); + +export default TorrentListRowCondensed; diff --git a/client/src/javascript/components/torrent-list/TorrentListRowExpanded.tsx b/client/src/javascript/components/torrent-list/TorrentListRowExpanded.tsx new file mode 100644 index 000000000..08fc9d1e2 --- /dev/null +++ b/client/src/javascript/components/torrent-list/TorrentListRowExpanded.tsx @@ -0,0 +1,126 @@ +import {FormattedNumber} from 'react-intl'; +import {forwardRef} from 'react'; +import {observer} from 'mobx-react'; + +import ProgressBar from '../general/ProgressBar'; +import SettingStore from '../../stores/SettingStore'; +import Size from '../general/Size'; +import TorrentListCell from './TorrentListCell'; +import TorrentListColumns from '../../constants/TorrentListColumns'; +import torrentStatusIcons from '../../util/torrentStatusIcons'; + +interface TorrentListRowExpandedProps { + className: string; + style: React.CSSProperties; + hash: string; + handleClick: (hash: string, event: React.MouseEvent) => void; + handleDoubleClick: (hash: string, event: React.MouseEvent) => void; + handleRightClick: (hash: string, event: React.MouseEvent) => void; + handleTouchStart: (event: React.TouchEvent) => void; + handleTouchEnd: (event: React.TouchEvent) => void; +} + +const TorrentListRowExpanded = observer( + forwardRef( + ( + { + className, + style, + hash, + handleClick, + handleDoubleClick, + handleRightClick, + handleTouchStart, + handleTouchEnd, + }: TorrentListRowExpandedProps, + ref, + ) => { + const columns = SettingStore.floodSettings.torrentListColumns; + + const primarySection: React.ReactNodeArray = [ + , + ]; + const secondarySection: React.ReactNodeArray = [ + , + , + , + ]; + const tertiarySection: React.ReactNodeArray = [ + ( + + + % +  —  + + + )} + showIcon + />, + ]; + const quaternarySection: React.ReactNodeArray = [ + ( + + )} + className="torrent__details__section torrent__details__section--quaternary" + classNameOverride + />, + ]; + + // Using a for loop to maximize performance. + for (let index = 0; index < columns.length; index += 1) { + const {id, visible} = columns[index]; + + if (TorrentListColumns[id] != null && visible) { + switch (id) { + case 'name': + break; + case 'downRate': + case 'upRate': + case 'eta': + break; + case 'downTotal': + case 'percentComplete': + break; + default: + tertiarySection.push(); + break; + } + } + } + + return ( +
  • handleClick(hash, e)} + onContextMenu={(e) => handleRightClick(hash, e)} + onDoubleClick={(e) => handleDoubleClick(hash, e)} + onTouchStart={handleTouchStart} + onTouchEnd={handleTouchEnd} + ref={ref}> +
    + {primarySection} +
    {secondarySection}
    +
    +
    {tertiarySection}
    + {quaternarySection} +
  • + ); + }, + ), +); + +export default TorrentListRowExpanded; diff --git a/client/src/javascript/components/views/Login.js b/client/src/javascript/components/views/Login.js deleted file mode 100644 index 6cc013684..000000000 --- a/client/src/javascript/components/views/Login.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import ApplicationView from '../layout/ApplicationView'; -import AuthForm from '../auth/AuthForm'; - -export default class LoginView extends React.Component { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/views/Login.tsx b/client/src/javascript/components/views/Login.tsx new file mode 100644 index 000000000..8c5d905ae --- /dev/null +++ b/client/src/javascript/components/views/Login.tsx @@ -0,0 +1,12 @@ +import {FC} from 'react'; + +import ApplicationView from '../layout/ApplicationView'; +import AuthForm from '../auth/AuthForm'; + +const LoginView: FC = () => ( + + + +); + +export default LoginView; diff --git a/client/src/javascript/components/views/Register.js b/client/src/javascript/components/views/Register.js deleted file mode 100644 index 0f4f50fee..000000000 --- a/client/src/javascript/components/views/Register.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import ApplicationView from '../layout/ApplicationView'; -import AuthForm from '../auth/AuthForm'; - -export default class LoginView extends React.Component { - render() { - return ( - - - - ); - } -} diff --git a/client/src/javascript/components/views/Register.tsx b/client/src/javascript/components/views/Register.tsx new file mode 100644 index 000000000..1381633b8 --- /dev/null +++ b/client/src/javascript/components/views/Register.tsx @@ -0,0 +1,12 @@ +import {FC} from 'react'; + +import ApplicationView from '../layout/ApplicationView'; +import AuthForm from '../auth/AuthForm'; + +const LoginView: FC = () => ( + + + +); + +export default LoginView; diff --git a/client/src/javascript/components/views/TorrentClientOverview.js b/client/src/javascript/components/views/TorrentClientOverview.js deleted file mode 100644 index 0d2aefbe1..000000000 --- a/client/src/javascript/components/views/TorrentClientOverview.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; - -import ActionBar from '../torrent-list/ActionBar'; -import Alerts from '../alerts/Alerts'; -import ApplicationContent from '../layout/ApplicationContent'; -import ApplicationPanel from '../layout/ApplicationPanel'; -import ApplicationView from '../layout/ApplicationView'; -import Modals from '../modals/Modals'; -import Sidebar from '../sidebar/Sidebar'; -import TorrentList from '../torrent-list/TorrentList'; - -export default class TorrentCLientOverview extends React.Component { - render() { - return ( - - - - - - - - - - - - ); - } -} diff --git a/client/src/javascript/components/views/TorrentClientOverview.tsx b/client/src/javascript/components/views/TorrentClientOverview.tsx new file mode 100644 index 000000000..311e383bd --- /dev/null +++ b/client/src/javascript/components/views/TorrentClientOverview.tsx @@ -0,0 +1,36 @@ +import {FC, lazy, useEffect} from 'react'; + +import ActionBar from '../torrent-list/ActionBar'; +import ApplicationContent from '../layout/ApplicationContent'; +import ApplicationPanel from '../layout/ApplicationPanel'; +import ApplicationView from '../layout/ApplicationView'; +import FloodActions from '../../actions/FloodActions'; +import Sidebar from '../sidebar/Sidebar'; +import TorrentList from '../torrent-list/TorrentList'; + +import 'overlayscrollbars/css/OverlayScrollbars.css'; + +const Alerts = lazy(() => import('../alerts/Alerts')); +const Modals = lazy(() => import('../modals/Modals')); + +const TorrentClientOverview: FC = () => { + useEffect(() => { + FloodActions.startActivityStream(); + }, []); + + return ( + + + + + + + + + + + + ); +}; + +export default TorrentClientOverview; diff --git a/client/src/javascript/constants/ActionTypes.js b/client/src/javascript/constants/ActionTypes.js deleted file mode 100644 index 94b6e74d4..000000000 --- a/client/src/javascript/constants/ActionTypes.js +++ /dev/null @@ -1,100 +0,0 @@ -import objectUtil from '@shared/util/objectUtil'; - -const actionTypes = [ - 'AUTH_CREATE_USER_SUCCESS', - 'AUTH_DELETE_USER_ERROR', - 'AUTH_DELETE_USER_SUCCESS', - 'AUTH_LIST_USERS_SUCCESS', - 'AUTH_LOGIN_ERROR', - 'AUTH_LOGIN_SUCCESS', - 'AUTH_LOGOUT_ERROR', - 'AUTH_LOGOUT_SUCCESS', - 'AUTH_REGISTER_ERROR', - 'AUTH_REGISTER_SUCCESS', - 'AUTH_VERIFY_ERROR', - 'AUTH_VERIFY_SUCCESS', - 'CLIENT_ADD_TORRENT_ERROR', - 'CLIENT_ADD_TORRENT_SUCCESS', - 'CLIENT_CHECK_HASH_ERROR', - 'CLIENT_CHECK_HASH_SUCCESS', - 'DISK_USAGE_CHANGE', - 'FLOOD_CLEAR_NOTIFICATIONS_ERROR', - 'FLOOD_CLEAR_NOTIFICATIONS_SUCCESS', - 'CLIENT_CONNECTION_TEST_ERROR', - 'CLIENT_CONNECTION_TEST_SUCCESS', - 'CLIENT_CONNECTIVITY_STATUS_CHANGE', - 'CLIENT_FETCH_TORRENT_TAXONOMY_ERROR', - 'CLIENT_FETCH_TORRENT_TAXONOMY_SUCCESS', - 'CLIENT_FETCH_TORRENT_DETAILS_ERROR', - 'CLIENT_FETCH_TORRENT_DETAILS_SUCCESS', - 'CLIENT_FETCH_TRANSFER_HISTORY_ERROR', - 'CLIENT_FETCH_TRANSFER_HISTORY_SUCCESS', - 'CLIENT_MOVE_TORRENTS_SUCCESS', - 'CLIENT_MOVE_TORRENTS_ERROR', - 'CLIENT_REMOVE_TORRENT_ERROR', - 'CLIENT_REMOVE_TORRENT_SUCCESS', - 'CLIENT_SET_FILE_PRIORITY_ERROR', - 'CLIENT_SET_FILE_PRIORITY_SUCCESS', - 'CLIENT_SET_TAXONOMY_ERROR', - 'CLIENT_SET_TAXONOMY_SUCCESS', - 'CLIENT_SET_THROTTLE_ERROR', - 'CLIENT_SET_THROTTLE_SUCCESS', - 'CLIENT_SET_TORRENT_PRIORITY_ERROR', - 'CLIENT_SET_TORRENT_PRIORITY_SUCCESS', - 'CLIENT_SETTINGS_FETCH_REQUEST_ERROR', - 'CLIENT_SETTINGS_FETCH_REQUEST_SUCCESS', - 'CLIENT_SETTINGS_SAVE_ERROR', - 'CLIENT_SETTINGS_SAVE_SUCCESS', - 'CLIENT_START_TORRENT_ERROR', - 'CLIENT_START_TORRENT_SUCCESS', - 'CLIENT_STOP_TORRENT_ERROR', - 'CLIENT_STOP_TORRENT_SUCCESS', - 'FLOOD_FETCH_NOTIFICATIONS_ERROR', - 'FLOOD_FETCH_NOTIFICATIONS_SUCCESS', - 'FLOOD_FETCH_MEDIAINFO_SUCCESS', - 'NOTIFICATION_COUNT_CHANGE', - 'SETTINGS_FEED_MONITOR_FEED_ADD_ERROR', - 'SETTINGS_FEED_MONITOR_FEED_ADD_SUCCESS', - 'SETTINGS_FEED_MONITOR_FEED_MODIFY_ERROR', - 'SETTINGS_FEED_MONITOR_FEED_MODIFY_SUCCESS', - 'SETTINGS_FEED_MONITOR_FEEDS_FETCH_ERROR', - 'SETTINGS_FEED_MONITOR_FEEDS_FETCH_SUCCESS', - 'SETTINGS_FEED_MONITORS_FETCH_ERROR', - 'SETTINGS_FEED_MONITORS_FETCH_SUCCESS', - 'SETTINGS_FEED_MONITOR_REMOVE_ERROR', - 'SETTINGS_FEED_MONITOR_REMOVE_SUCCESS', - 'SETTINGS_FEED_MONITOR_RULE_ADD_ERROR', - 'SETTINGS_FEED_MONITOR_RULE_ADD_SUCCESS', - 'SETTINGS_FEED_MONITOR_RULES_FETCH_ERROR', - 'SETTINGS_FEED_MONITOR_RULES_FETCH_SUCCESS', - 'SETTINGS_FEED_MONITOR_ITEMS_FETCH_ERROR', - 'SETTINGS_FEED_MONITOR_ITEMS_FETCH_SUCCESS', - 'SETTINGS_FETCH_REQUEST_SUCCESS', - 'SETTINGS_FETCH_REQUEST_ERROR', - 'SETTINGS_SAVE_REQUEST_SUCCESS', - 'SETTINGS_SAVE_REQUEST_ERROR', - 'TAXONOMY_DIFF_CHANGE', - 'TAXONOMY_FULL_UPDATE', - 'TORRENT_LIST_DIFF_CHANGE', - 'TORRENT_LIST_FULL_UPDATE', - 'TRANSFER_HISTORY_FULL_UPDATE', - 'TRANSFER_SUMMARY_DIFF_CHANGE', - 'TRANSFER_SUMMARY_FULL_UPDATE', - 'UI_CLICK_TORRENT', - 'UI_CLICK_TORRENT_DETAILS', - 'UI_DISPLAY_MODAL', - 'UI_DISMISS_CONTEXT_MENU', - 'UI_DISPLAY_CONTEXT_MENU', - 'UI_DISPLAY_DROPDOWN_MENU', - 'UI_LATEST_TORRENT_LOCATION_REQUEST_ERROR', - 'UI_LATEST_TORRENT_LOCATION_REQUEST_SUCCESS', - 'UI_SET_TORRENT_SEARCH_FILTER', - 'UI_SET_TORRENT_SORT', - 'UI_SET_TORRENT_STATUS_FILTER', - 'UI_SET_TORRENT_TAG_FILTER', - 'UI_SET_TORRENT_TRACKER_FILTER', - 'UI_SORT_PROPS_REQUEST_SUCCESS', - 'UI_SORT_PROPS_REQUEST_ERROR', -]; - -export default objectUtil.createStringMapFromArray(actionTypes); diff --git a/client/src/javascript/constants/Alerts.js b/client/src/javascript/constants/Alerts.js deleted file mode 100644 index 012f335df..000000000 --- a/client/src/javascript/constants/Alerts.js +++ /dev/null @@ -1,29 +0,0 @@ -const ALERTS = { - 'alert.torrent.add': `Successfully added {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.add.failed': `Failed to add {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.move': `Successfully moved {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.move.failed': `Failed to move {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.remove': `Successfully removed {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.remove.failed': `Failed to remove {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.settings.saved': 'Successfully saved settings.', -}; - -export default ALERTS; diff --git a/client/src/javascript/constants/EventTypes.js b/client/src/javascript/constants/EventTypes.js deleted file mode 100644 index 44a84bd90..000000000 --- a/client/src/javascript/constants/EventTypes.js +++ /dev/null @@ -1,84 +0,0 @@ -import objectUtil from '@shared/util/objectUtil'; - -const eventTypes = [ - 'ALERTS_CHANGE', - 'AUTH_CREATE_USER_SUCCESS', - 'AUTH_DELETE_USER_ERROR', - 'AUTH_DELETE_USER_SUCCESS', - 'AUTH_LIST_USERS_SUCCESS', - 'AUTH_LOGIN_ERROR', - 'AUTH_LOGIN_SUCCESS', - 'AUTH_REGISTER_ERROR', - 'AUTH_REGISTER_SUCCESS', - 'AUTH_VERIFY_ERROR', - 'AUTH_VERIFY_SUCCESS', - 'CLIENT_CONNECTION_STATUS_CHANGE', - 'CLIENT_ADD_TORRENT_ERROR', - 'CLIENT_ADD_TORRENT_SUCCESS', - 'CLIENT_FETCH_TORRENT_TAXONOMY_ERROR', - 'CLIENT_FETCH_TORRENT_TAXONOMY_SUCCESS', - 'CLIENT_SET_FILE_PRIORITY_ERROR', - 'CLIENT_SET_FILE_PRIORITY_SUCCESS', - 'CLIENT_SET_THROTTLE_ERROR', - 'CLIENT_SET_THROTTLE_SUCCESS', - 'CLIENT_SET_TORRENT_PRIORITY_ERROR', - 'CLIENT_SET_TORRENT_PRIORITY_SUCCESS', - 'CLIENT_MOVE_TORRENTS_REQUEST_ERROR', - 'CLIENT_MOVE_TORRENTS_SUCCESS', - 'CLIENT_SETTINGS_FETCH_REQUEST_ERROR', - 'CLIENT_SETTINGS_FETCH_REQUEST_SUCCESS', - 'CLIENT_SETTINGS_SAVE_REQUEST_ERROR', - 'CLIENT_SETTINGS_SAVE_REQUEST_SUCCESS', - 'CLIENT_TORRENTS_REQUEST_ERROR', - 'CLIENT_TORRENT_STATUS_COUNT_CHANGE', - 'CLIENT_TORRENT_STATUS_COUNT_REQUEST_ERROR', - 'CLIENT_TORRENT_TRACKER_COUNT_CHANGE', - 'CLIENT_TORRENT_TRACKER_COUNT_REQUEST_ERROR', - 'CLIENT_TORRENTS_REQUEST_SUCCESS', - 'CLIENT_TORRENT_DETAILS_CHANGE', - 'CLIENT_TRANSFER_DATA_REQUEST_SUCCESS', - 'CLIENT_TRANSFER_DATA_REQUEST_ERROR', - 'CLIENT_TRANSFER_HISTORY_REQUEST_SUCCESS', - 'CLIENT_TRANSFER_HISTORY_REQUEST_ERROR', - 'CLIENT_TRANSFER_SUMMARY_CHANGE', - 'DISK_USAGE_CHANGE', - 'FLOOD_FETCH_MEDIAINFO_SUCCESS', - 'NOTIFICATIONS_FETCH_ERROR', - 'NOTIFICATIONS_FETCH_SUCCESS', - 'NOTIFICATIONS_COUNT_CHANGE', - 'SETTINGS_CHANGE', - 'SETTINGS_SAVE_REQUEST_ERROR', - 'SETTINGS_SAVE_REQUEST_SUCCESS', - 'SETTINGS_FEED_MONITOR_FEED_ADD_ERROR', - 'SETTINGS_FEED_MONITOR_FEED_ADD_SUCCESS', - 'SETTINGS_FEED_MONITOR_RULE_ADD_ERROR', - 'SETTINGS_FEED_MONITOR_RULE_ADD_SUCCESS', - 'SETTINGS_FEED_MONITOR_REMOVE_ERROR', - 'SETTINGS_FEED_MONITOR_REMOVE_SUCCESS', - 'SETTINGS_FEED_MONITORS_FETCH_ERROR', - 'SETTINGS_FEED_MONITORS_FETCH_SUCCESS', - 'SETTINGS_FEED_MONITOR_ITEMS_FETCH_ERROR', - 'SETTINGS_FEED_MONITOR_ITEMS_FETCH_SUCCESS', - 'SETTINGS_FETCH_REQUEST_ERROR', - 'SETTINGS_FETCH_REQUEST_SUCCESS', - 'UI_CONTEXT_MENU_CHANGE', - 'UI_DEPENDENCIES_CHANGE', - 'UI_DEPENDENCIES_LOADED', - 'UI_DROPDOWN_MENU_CHANGE', - 'UI_MODAL_DISMISSED', - 'UI_MODAL_CHANGE', - 'UI_LATEST_TORRENT_LOCATION_CHANGE', - 'UI_TORRENT_DETAILS_HASH_CHANGE', - 'UI_TORRENT_DETAILS_OPEN_CHANGE', - 'UI_TORRENT_SELECTION_CHANGE', - 'UI_TORRENTS_FILTER_CHANGE', - 'UI_TORRENTS_FILTER_CLEAR', - 'UI_TORRENTS_FILTER_STATUS_CHANGE', - 'UI_TORRENTS_FILTER_TAG_CHANGE', - 'UI_TORRENTS_FILTER_TRACKER_CHANGE', - 'UI_TORRENTS_FILTER_SEARCH_CHANGE', - 'UI_TORRENTS_LIST_FILTERED', - 'UI_TORRENTS_SORT_CHANGE', -]; - -export default objectUtil.createStringMapFromArray(eventTypes); diff --git a/client/src/javascript/constants/Languages.js b/client/src/javascript/constants/Languages.js deleted file mode 100644 index bc5f31eac..000000000 --- a/client/src/javascript/constants/Languages.js +++ /dev/null @@ -1,28 +0,0 @@ -const LANGUAGES = { - en: { - defaultMessage: 'English', - id: 'locale.language.en', - }, - es: { - defaultMessage: 'Spanish', - id: 'locale.language.es', - }, - fr: { - defaultMessage: 'French', - id: 'locale.language.fr', - }, - nl: { - defaultMessage: 'Nederlands', - id: 'locale.language.nl', - }, - ko: { - defaultMessage: 'Korean', - id: 'locale.language.ko', - }, - zh: { - defaultMessage: 'Chinese', - id: 'locale.language.zh', - }, -}; - -export default LANGUAGES; diff --git a/client/src/javascript/constants/Languages.ts b/client/src/javascript/constants/Languages.ts new file mode 100644 index 000000000..b5521fb96 --- /dev/null +++ b/client/src/javascript/constants/Languages.ts @@ -0,0 +1,31 @@ +const Languages = { + auto: { + id: 'locale.language.auto', + }, + en: 'English', + cs: 'Čeština', + de: 'Deutsch', + es: 'Español', + fr: 'Français', + it: 'italiano', + hu: 'magyar nyelv', + nl: 'Nederlands', + no: 'norsk', + pl: 'Polskie', + pt: 'português', + ru: 'русский язык', + ro: 'Romanian', + sv: 'svenska', + uk: 'українська мова', + ko: '한국어', + ja: '日本語', + 'zh-Hans': '中文(简体)', + 'zh-Hant': '中文(繁體)', + ar: 'اَلْعَرَبِيَّةُ', + translate: { + id: 'locale.language.translate', + }, +} as const; + +export default Languages; +export type Language = keyof typeof Languages; diff --git a/client/src/javascript/constants/PriorityLevels.js b/client/src/javascript/constants/PriorityLevels.js deleted file mode 100644 index 0d16e7ed1..000000000 --- a/client/src/javascript/constants/PriorityLevels.js +++ /dev/null @@ -1,15 +0,0 @@ -const PRIORITY_LEVELS = { - file: { - 0: 'DONT_DOWNLOAD', - 1: 'NORMAL', - 2: 'HIGH', - }, - torrent: { - 0: 'DONT_DOWNLOAD', - 1: 'LOW', - 2: 'NORMAL', - 3: 'HIGH', - }, -}; - -export default PRIORITY_LEVELS; diff --git a/client/src/javascript/constants/PriorityLevels.ts b/client/src/javascript/constants/PriorityLevels.ts new file mode 100644 index 000000000..ac428b572 --- /dev/null +++ b/client/src/javascript/constants/PriorityLevels.ts @@ -0,0 +1,15 @@ +const PriorityLevels = { + file: { + 0: 'DONT_DOWNLOAD', + 1: 'NORMAL', + 2: 'HIGH', + }, + torrent: { + 0: 'DONT_DOWNLOAD', + 1: 'LOW', + 2: 'NORMAL', + 3: 'HIGH', + }, +} as const; + +export default PriorityLevels; diff --git a/client/src/javascript/constants/TorrentContextMenuActions.ts b/client/src/javascript/constants/TorrentContextMenuActions.ts new file mode 100644 index 000000000..85e9fb202 --- /dev/null +++ b/client/src/javascript/constants/TorrentContextMenuActions.ts @@ -0,0 +1,38 @@ +const TorrentContextMenuActions = { + start: { + id: 'torrents.list.context.start', + }, + stop: { + id: 'torrents.list.context.stop', + }, + remove: { + id: 'torrents.list.context.remove', + }, + checkHash: { + id: 'torrents.list.context.check.hash', + }, + setTaxonomy: { + id: 'torrents.list.context.set.tags', + }, + move: { + id: 'torrents.list.context.move', + }, + setTrackers: { + id: 'torrents.list.context.set.trackers', + }, + torrentDetails: { + id: 'torrents.list.context.details', + }, + torrentDownload: { + id: 'torrents.list.context.download', + }, + generateMagnet: { + id: 'torrents.list.context.generate.magnet', + }, + setPriority: { + id: 'torrents.list.context.priority', + }, +} as const; + +export default TorrentContextMenuActions; +export type TorrentContextMenuAction = keyof typeof TorrentContextMenuActions; diff --git a/client/src/javascript/constants/TorrentListColumns.ts b/client/src/javascript/constants/TorrentListColumns.ts new file mode 100644 index 000000000..a408faa1b --- /dev/null +++ b/client/src/javascript/constants/TorrentListColumns.ts @@ -0,0 +1,62 @@ +const TorrentListColumns = { + dateAdded: { + id: 'torrents.properties.date.added', + }, + downRate: { + id: 'torrents.properties.download.speed', + }, + downTotal: { + id: 'torrents.properties.download.total', + }, + eta: { + id: 'torrents.properties.eta', + }, + name: { + id: 'torrents.properties.name', + }, + peers: { + id: 'torrents.properties.peers', + }, + percentComplete: { + id: 'torrents.properties.percentage', + }, + ratio: { + id: 'torrents.properties.ratio', + }, + seeds: { + id: 'torrents.properties.seeds', + }, + sizeBytes: { + id: 'torrents.properties.size', + }, + tags: { + id: 'torrents.properties.tags', + }, + upRate: { + id: 'torrents.properties.upload.speed', + }, + upTotal: { + id: 'torrents.properties.upload.total', + }, + dateCreated: { + id: 'torrents.properties.creation.date', + }, + directory: { + id: 'torrents.properties.directory', + }, + hash: { + id: 'torrents.properties.hash', + }, + isPrivate: { + id: 'torrents.properties.is.private', + }, + message: { + id: 'torrents.properties.tracker.message', + }, + trackerURIs: { + id: 'torrents.properties.trackers', + }, +} as const; + +export default TorrentListColumns; +export type TorrentListColumn = keyof typeof TorrentListColumns; diff --git a/client/src/javascript/constants/TorrentProperties.js b/client/src/javascript/constants/TorrentProperties.js deleted file mode 100644 index 9eef6f3bc..000000000 --- a/client/src/javascript/constants/TorrentProperties.js +++ /dev/null @@ -1,88 +0,0 @@ -const torrentProperties = { - dateAdded: { - id: 'torrents.properties.date.added', - defaultMessage: 'Date Added', - }, - downRate: { - id: 'torrents.properties.download.speed', - defaultMessage: 'Download Speed', - }, - downTotal: { - id: 'torrents.properties.download.total', - defaultMessage: 'Downloaded', - }, - eta: { - id: 'torrents.properties.eta', - defaultMessage: 'ETA', - }, - name: { - id: 'torrents.properties.name', - defaultMessage: 'Name', - }, - peers: { - id: 'torrents.properties.peers', - defaultMessage: 'Peers', - }, - percentComplete: { - id: 'torrents.properties.percentage', - defaultMessage: 'Percent Complete', - }, - ratio: { - id: 'torrents.properties.ratio', - defaultMessage: 'Ratio', - }, - seeds: { - id: 'torrents.properties.seeds', - defaultMessage: 'Seeds', - }, - sizeBytes: { - id: 'torrents.properties.size', - defaultMessage: 'File Size', - }, - tags: { - id: 'torrents.properties.tags', - defaultMessage: 'Tags', - }, - upRate: { - id: 'torrents.properties.upload.speed', - defaultMessage: 'Upload Speed', - }, - upTotal: { - id: 'torrents.properties.upload.total', - defaultMessage: 'Uploaded', - }, - dateCreated: { - id: 'torrents.properties.creation.date', - defaultMessage: 'Creation Date', - }, - basePath: { - id: 'torrents.properties.base.path', - defaultMessage: 'Base Path', - }, - ignoreScheduler: { - id: 'torrents.properties.ignore.schedule', - defaultMessage: 'Ignore Scheduler', - }, - comment: { - id: 'torrents.properties.comment', - defaultMessage: 'Comment', - }, - hash: { - id: 'torrents.properties.hash', - defaultMessage: 'Hash', - }, - isPrivate: { - id: 'torrents.properties.is.private', - defaultMessage: 'Private', - }, - message: { - id: 'torrents.properties.tracker.message', - defaultMessage: 'Tracker Message', - }, - trackerURIs: { - id: 'torrents.properties.trackers', - defaultMessage: 'Trackers', - }, -}; - -export default torrentProperties; diff --git a/client/src/javascript/dispatcher/AppDispatcher.js b/client/src/javascript/dispatcher/AppDispatcher.js deleted file mode 100644 index 9520ae712..000000000 --- a/client/src/javascript/dispatcher/AppDispatcher.js +++ /dev/null @@ -1,21 +0,0 @@ -import {Dispatcher} from 'flux'; - -class FloodDispatcher extends Dispatcher { - dispatchUIAction(action) { - if (action.type == null) { - console.warn('Undefined action.type', action); - } - this.dispatch({source: 'UI_ACTION', action}); - } - - dispatchServerAction(action) { - if (action.type == null) { - console.warn('Undefined action.type', action); - } - this.dispatch({source: 'SERVER_ACTION', action}); - } -} - -const AppDispatcher = new FloodDispatcher(); - -export default AppDispatcher; diff --git a/client/src/javascript/i18n/compiled/af.json b/client/src/javascript/i18n/compiled/af.json new file mode 100644 index 000000000..876af72fd --- /dev/null +++ b/client/src/javascript/i18n/compiled/af.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Remove Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Successfully saved settings." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Successfully added " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Failed to add " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Successfully moved " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Failed to move " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Successfully removed " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Failed to remove " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Add User" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Create Account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Create an account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Welcome to Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Current User" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Username cannot be empty." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log In" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Login" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in to your account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "User is not Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Password" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "User Accounts" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Username" + } + ], + "button.add": [ + { + "type": 0, + "value": "Add" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancel" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "New" + } + ], + "button.no": [ + { + "type": 0, + "value": "No" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Save Settings" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Save" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Adding..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Yes" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Connection could not be verified." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Connectivity Issue" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent List" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Transfer History" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Add Download Rule" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Add Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Apply Tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Browse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclude" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exclude Pattern" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Existing Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Existing Rules" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Label" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Match" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matches" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match Pattern" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "No feeds available." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "No feeds defined." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No items matching search term." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "No rules defined." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Select Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start on load" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Download Rules" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Days" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Hours" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutes" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "The interval must be a positive integer." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Invalid regular expression." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "You must select a feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "You must specify a destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "You must specify a label." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "You must specify a valid feed URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Empty directory." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood does not have permission to read this directory." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "This path does not exist. It will be created." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Fetching directory structure..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Parent Directory" + } + ], + "filter.all": [ + { + "type": 0, + "value": "All" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Active" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Checking" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloading" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Error" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactive" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stopped" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filter by Status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filter by Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filter by Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Untagged" + } + ], + "general.ago": [ + { + "type": 0, + "value": "ago" + } + ], + "general.at": [ + { + "type": 0, + "value": "at" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copied" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copy" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "of" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatic" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Fetching..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Output" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Clear All" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Showing" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Error Reported" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Finished Downloading" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Don't Download" + } + ], + "priority.high": [ + { + "type": 0, + "value": "High" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Low" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "About Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download Slots Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slot Availability" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload Slots Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown Presets: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown Presets: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global Download Rate Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global Upload Rate Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Transfer Rate Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Enable DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralized Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Incoming Connections" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Reported IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximum HTTP Connections" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Enable Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Peers Desired" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximum Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimum Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximum Peers Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum Peers Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Open Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Randomize Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Range" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verify Hash on Completion" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Default Download Directory" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximum Open Files" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memory" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Max Memory Usage" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentication" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandwidth" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectivity" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Settings" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resources" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "User Interface" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Language" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Locale" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Enabled" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In the expanded view, tags work best at the end of the list." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent List Display" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Size" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Condensed View" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Expanded View" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log Out" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Settings" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Speed Limits" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Search torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Unlimited" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Free" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Used" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "of" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Add Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "or click to browse" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Drop some files here," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "By File" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL or Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "By URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Use as Base Path" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Details" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Files" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download File" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download Files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Loading file detail..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connected of " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "General" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfer" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "None" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Scheduler" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignored" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Size" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "There is no peer data for this torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected file" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Set Priority" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "There is no tracker data for this torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Clear Filters" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Check Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priority" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Remove" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "No torrents to display." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Set Location" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Setting..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Check hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Move data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Download Speed" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignore Scheduler" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Name" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percent Complete" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "File Size" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Upload Speed" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Remove Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Are you sure you want to remove " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Delete data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "You haven't selected any torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Enter tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sort By" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "hr" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/ar.json b/client/src/javascript/i18n/compiled/ar.json new file mode 100644 index 000000000..3d2080627 --- /dev/null +++ b/client/src/javascript/i18n/compiled/ar.json @@ -0,0 +1,2604 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "إضافة تورنت" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "إزالة التورنت" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "ابدأ التورينت" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "إيقاف التورنت" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "تم حفظ الإعدادات بنجاح." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "تمت إضافة " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "فشل في إضافة " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "تم نقل " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " بنجاح " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "فشل نقل " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "تم بنجاح إزالة " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "فشل في إزالة " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "تورنت" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "إضافة مستخدم" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "المشرف" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "إنشاء حساب" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "إنشاء حساب" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "مرحبا بكم في الفيضان!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "المستخدم الحالي" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "كلمة المرور لا يمكن أن تكون فارغة." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "اسم المستخدم لا يمكن أن يكون فارغاً." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "تسجيل الدخول" + } + ], + "auth.login": [ + { + "type": 0, + "value": "تسجيل الدخول" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "تسجيل الدخول إلى حسابك." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "المستخدم ليس مشرف" + } + ], + "auth.password": [ + { + "type": 0, + "value": "كلمة المرور" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "حسابات المستخدم" + } + ], + "auth.username": [ + { + "type": 0, + "value": "اسم المستخدم" + } + ], + "button.add": [ + { + "type": 0, + "value": "إضافة" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "إلغاء" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "تنزيل" + } + ], + "button.new": [ + { + "type": 0, + "value": "جديد" + } + ], + "button.no": [ + { + "type": 0, + "value": "لا" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "حفظ الإعدادات" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "حفظ" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "إضافة..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "نعم" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "لا يمكن الاتصال بالعميل" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "تعذر التحقق من الاتصال." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "العميل" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "لا يمكن أن تكون إعدادات الاتصال فارغة." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "كلمة المرور" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "كلمة المرور" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "الرابط" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "عنوان URL ل qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "اسم المستخدم" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "اسم المستخدم" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "تورنت" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "المضيف" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "اسم المضيف أو IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "المنفذ" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "المنفذ" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "المسار" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "المسار إلى المقبس" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "نوع الاتصال" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "لا يمكن الاتصال بالعميل. الرجاء تحديث إعدادات الاتصال." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "مشكلة في الاتصال" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "الإشعارات" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "قائمة التورينت" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "تصنيف تورنت" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "سجل نقل البيانات" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "تفاصيل معدل نقل البيانات" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "إضافة قاعدة تحميل" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "إضافة موجز ويب" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "تغذية قابلة للتطبيق" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "تطبيق العلامات" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "تصفح الخلاصات" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "استبعاد" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "استبعاد النمط" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "التحديثات الموجودة" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "القواعد الحالية" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "الفاصل" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "تسمية" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "المباراة" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " تطابق" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " يتطابق" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "نمط المطابقة" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "لا توجد تغذية متاحة." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "لم يتم تعريف أي تغذية." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "لا توجد عناصر مطابقة لمصطلح البحث." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "لا توجد قواعد معرفة." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "ريريكس" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "مصطلح البحث" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "مصطلح البحث" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "حدد موجز الويب" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "الفاصل" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "البدء عند التحميل" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "قواعد التحميل" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "التحديثات" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "تغذية تورنت" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "الوسوم" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "اختبار نمط المطابقة" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "أيام" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "الساعات" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "دقائق" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "وجهة تورنت" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "الرابط" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "الفاصل الزمني يجب أن يكون عددا صحيحا إيجابيا." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "تعبير عادي غير صالح." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "يجب عليك تحديد التغذية." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "يجب عليك تحديد الوجهة." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "يجب عليك تحديد التسمية." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "يجب عليك تحديد رابط تغذية صالح." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "مجلد فارغ." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "الفيضان ليس لديه الصلاحية لقراءة هذا الدليل." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "هذا المسار غير موجود. سيتم إنشاؤه." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "حدث خطأ غير معروف. الرجاء المحاولة مرة أخرى." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "جلب بنية الدليل..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "الدليل الأصلي" + } + ], + "filter.all": [ + { + "type": 0, + "value": "الكل" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "نشط" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "التحقق" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "تنزيل" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "خطأ" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "غير نشط" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "البذور" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "توقفت" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "تصفية حسب الحالة" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "تصفية حسب العلامة" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "تصفية حسب التتبع" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "غير موسومة" + } + ], + "general.ago": [ + { + "type": 0, + "value": "مضت" + } + ], + "general.at": [ + { + "type": 0, + "value": "في" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "منسوخ" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "نسخ" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "حدث خطأ غير معروف" + } + ], + "general.of": [ + { + "type": 0, + "value": "من" + } + ], + "general.to": [ + { + "type": 0, + "value": "إلى" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "تلقائي" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "حدث خطأ أثناء تشغيل mediainfo على الخادم. تحقق من أن mediainfo مثبت ومتاح في PATH للفيضان." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "إحضار ..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "ناتج ميداينفو" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "مسح الكل" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "قائمة انتظار عنصر التغذية" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "عرض" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "تم الإبلاغ عن خطأ" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "انتهى التحميل" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "عدم التحميل" + } + ], + "priority.high": [ + { + "type": 0, + "value": "مرتفع" + } + ], + "priority.low": [ + { + "type": 0, + "value": "منخفض" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "عادي" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "حول الفيضان" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "تحميل الفتحات العالمية" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "تنزيل الفتحات لكل تورينت" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "توفر الخانة" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "تحميل خانات عامة" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "تحميل الفتحات لكل تورينت" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "المنسدلة مسبقاً: تحميل" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "القائمة المنسدلة مسبقاً: تحميل" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "خنق معدل التحميل العالمي" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "خنق معدل التحميل العالمي" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "خانات معدل التحويل" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "تمكين DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "منفذ DHT" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "اكتشاف الند اللامركزي" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "اتصالات واردة" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "اسم IP/المضيف المبلغ عنه" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "الحد الأقصى لاتصالات HTTP" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "تمكين تبادل الأقران" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "الند المنشود" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "أقران" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "الحد الأقصى للنظراء" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "الحد الأدنى للنظراء" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "الحد الأقصى لبذور النظراء" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "الحد الأدنى لبذور الند" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "فتح المنفذ" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "تصوير المنفذ عشوائي" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "نطاق المنفذ" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "نقاط استخدام القرص" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "إظهار" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "التحقق من التجزئة عند الاكتمال" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "دليل التحميل الافتراضي" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "قرص" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "الحد الأقصى للملفات المفتوحة" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "الذاكرة" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "الحد الأقصى لاستخدام الذاكرة" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "حول" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "المصادقة" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "عرض التردد" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "الاتصال" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "استخدام القرص" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "الإعدادات" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "الموارد" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "واجهة المستخدم" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "عناصر قائمة السياق" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "أعمدة تفاصيل تورنت" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "اللغة" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "محلي" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "إظهار" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "تمكين" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "ومن وجهة النظر الموسعة، فإن العلامات تعمل على أفضل وجه في نهاية القائمة." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "عرض قائمة التورينت" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "حجم التورنت" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "عرض مكثف" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "عرض موسع" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "التحديثات" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "تسجيل الخروج" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "الإعدادات" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "حدود السرعة" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "البحث عن التورنت" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "تحميل" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "تحميل" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "تم التحميل" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "تم الرفع" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "غير محدود" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "مجاني" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "استخدام القرص" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "المجموع" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "مستخدم" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "من" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "إضافة تورنت" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "قيمة cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "الكعكات" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "الوجهة" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "الوجهة" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "إضافة تورينت" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "ابدأ التورينت" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "إنشاء" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "أو انقر لتصفح" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "إسقاط بعض الملفات هنا،" + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "حسب الملف" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "رابط تورينت أو رابط الـ Magnet" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "حسب عنوان URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "الوسوم" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "تورينتس" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "اسم ملف أساسي أو دليل تورنت اختياري" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "اسم القاعدة" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "التعليق الاختياري في ملف تورنت" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "تعليق" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "إدخال المصدر الاختياري في Infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "مصدر المعلومات" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "خاص" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "المصدر" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "العلامات في الفيضان. غير مضافة إلى التورنت التي تم إنشاؤها." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "رابط التتبع" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "المتتبعون" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "استخدام كمسار أساسي" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "مكتمل" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "إيقاف" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "ابدأ" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "توقف" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "التفاصيل" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "الملفات" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "تحميل الملف" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "تحميل الملفات" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "تحميل تفاصيل الملف..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "تعليق" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " متصل بـ " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "أضيف" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "تاريخ الإنشاء" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "تم التحميل" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "مساحة القرص الحرة" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "التجزئة" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "عام" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "تورنت" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "المتتبع" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "نقل" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "الموقع" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "لا" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "أقران" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "جدولة" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "تجاهل" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "عقيب" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "بذور" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "الحجم" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "الوسوم" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "رسالة تتبع" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "نوع" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "خاص" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "عامة" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "أقران" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "لا توجد بيانات أقران لهذا التورنت." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " ملف محدد" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " ملفات محددة" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "تعيين الأولوية" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "المتتبعون" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "لا توجد بيانات تتبع لهذا التورنت." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "نوع" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "لا يمكن الاتصال بالعميل." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "مسح الفلاتر" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "تحقق من التجزئة" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "تفاصيل التورنت" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "تنزيل" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "تعيين موقع التورنت" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "إيقاف" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "الأولوية" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "إزالة" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "تعيين العلامات" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "تعيين التتبع" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "ابدأ" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "توقف" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "إسقاط الملفات هنا لإضافتها." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "لا يوجد سلالات لعرضها." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "تعيين الموقع" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "إعداد..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "تحقق من التجزئة" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "نقل البيانات" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "تعيين موقع التورنت" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "تعليق" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "تاريخ الإنشاء" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "أضيف" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "سرعة التنزيل" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "تم التحميل" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "مساحة القرص الحرة" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "التجزئة" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "تجاهل المجدول" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "خاص" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "الاسم" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "أقران" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "اكتمل النسبة" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "النسبة" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "بذور" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "حجم الملف" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "الوسوم" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "رسالة تتبع" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "المتتبعون" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "سرعة التحميل" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "تم الرفع" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "إزالة التورنت" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "هل أنت متأكد من أنك تريد إزالة " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " تورنت" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " تورنت" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "حذف البيانات" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "لم تقم باختيار أي تورنات." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "تعيين العلامات" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "أدخل العلامات" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "تعيين العلامات" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "تعيين التتبع" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "أدخل متتبع" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "تعيين التتبع" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "جاري تحميل التتبع..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "الترتيب حسب" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "ب" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "جيجابايت" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "كيلوبايت" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "ميغابايت" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "تيلبايت" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/ث" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "د" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "ساعة" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "د" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "ث" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "تأجج" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/ca.json b/client/src/javascript/i18n/compiled/ca.json new file mode 100644 index 000000000..876af72fd --- /dev/null +++ b/client/src/javascript/i18n/compiled/ca.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Remove Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Successfully saved settings." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Successfully added " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Failed to add " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Successfully moved " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Failed to move " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Successfully removed " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Failed to remove " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Add User" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Create Account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Create an account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Welcome to Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Current User" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Username cannot be empty." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log In" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Login" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in to your account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "User is not Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Password" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "User Accounts" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Username" + } + ], + "button.add": [ + { + "type": 0, + "value": "Add" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancel" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "New" + } + ], + "button.no": [ + { + "type": 0, + "value": "No" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Save Settings" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Save" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Adding..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Yes" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Connection could not be verified." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Connectivity Issue" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent List" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Transfer History" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Add Download Rule" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Add Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Apply Tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Browse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclude" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exclude Pattern" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Existing Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Existing Rules" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Label" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Match" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matches" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match Pattern" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "No feeds available." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "No feeds defined." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No items matching search term." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "No rules defined." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Select Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start on load" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Download Rules" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Days" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Hours" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutes" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "The interval must be a positive integer." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Invalid regular expression." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "You must select a feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "You must specify a destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "You must specify a label." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "You must specify a valid feed URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Empty directory." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood does not have permission to read this directory." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "This path does not exist. It will be created." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Fetching directory structure..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Parent Directory" + } + ], + "filter.all": [ + { + "type": 0, + "value": "All" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Active" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Checking" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloading" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Error" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactive" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stopped" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filter by Status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filter by Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filter by Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Untagged" + } + ], + "general.ago": [ + { + "type": 0, + "value": "ago" + } + ], + "general.at": [ + { + "type": 0, + "value": "at" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copied" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copy" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "of" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatic" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Fetching..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Output" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Clear All" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Showing" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Error Reported" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Finished Downloading" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Don't Download" + } + ], + "priority.high": [ + { + "type": 0, + "value": "High" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Low" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "About Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download Slots Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slot Availability" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload Slots Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown Presets: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown Presets: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global Download Rate Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global Upload Rate Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Transfer Rate Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Enable DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralized Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Incoming Connections" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Reported IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximum HTTP Connections" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Enable Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Peers Desired" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximum Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimum Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximum Peers Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum Peers Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Open Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Randomize Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Range" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verify Hash on Completion" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Default Download Directory" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximum Open Files" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memory" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Max Memory Usage" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentication" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandwidth" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectivity" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Settings" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resources" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "User Interface" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Language" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Locale" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Enabled" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In the expanded view, tags work best at the end of the list." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent List Display" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Size" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Condensed View" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Expanded View" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log Out" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Settings" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Speed Limits" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Search torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Unlimited" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Free" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Used" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "of" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Add Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "or click to browse" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Drop some files here," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "By File" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL or Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "By URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Use as Base Path" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Details" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Files" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download File" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download Files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Loading file detail..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connected of " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "General" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfer" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "None" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Scheduler" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignored" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Size" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "There is no peer data for this torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected file" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Set Priority" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "There is no tracker data for this torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Clear Filters" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Check Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priority" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Remove" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "No torrents to display." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Set Location" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Setting..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Check hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Move data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Download Speed" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignore Scheduler" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Name" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percent Complete" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "File Size" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Upload Speed" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Remove Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Are you sure you want to remove " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Delete data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "You haven't selected any torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Enter tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sort By" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "hr" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/cs.json b/client/src/javascript/i18n/compiled/cs.json new file mode 100644 index 000000000..ac52ffd85 --- /dev/null +++ b/client/src/javascript/i18n/compiled/cs.json @@ -0,0 +1,2604 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Přidat Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Odebrat Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Spustit Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Zastavit Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Nastavení bylo úspěšně uloženo." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Úspěšně přidáno " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Nepodařilo se přidat " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Úspěšně přesunuto " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Nepodařilo se přesunout " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Úspěšně odstraněno " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Nepodařilo se odstranit " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Přidat uživatele" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Vytvořit účet" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Vytvořit účet" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Vítejte v záplavě!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Aktuální uživatel" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Heslo nemůže být prázdné." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Uživatelské jméno nemůže být prázdné." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Vyčistit" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Přihlásit se" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Přihlásit se" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Přihlaste se ke svému účtu." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Uživatel není správce" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Heslo" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Uživatelské účty" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Uživatelské jméno" + } + ], + "button.add": [ + { + "type": 0, + "value": "Přidat" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Zrušit" + } + ], + "button.close": [ + { + "type": 0, + "value": "Zavřít" + } + ], + "button.download": [ + { + "type": 0, + "value": "Stáhnout" + } + ], + "button.new": [ + { + "type": 0, + "value": "Nové" + } + ], + "button.no": [ + { + "type": 0, + "value": "Ne" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Opakovat" + } + ], + "button.save": [ + { + "type": 0, + "value": "Uložit nastavení" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Uložit" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Přidávání..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Ano" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Aktualizovat nastavení připojení klienta" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Opakovat s nastavením připojení aktuálního klienta" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Nelze se připojit ke klientu" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Pokud problém přetrvává, prosím kontaktujte administrátora." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Nelze ověřit spojení." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Klient" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Nastavení připojení nemůže být prázdné." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Heslo" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Heslo" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL webového API qBittorrentu" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Uživatelské jméno" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Uživatelské jméno" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Hostitel" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname nebo IP adresa" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Cesta" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Cesta k socketu" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Typ připojení" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Vystavení rTorrentu prostřednictvím TCP může umožnit eskalaci práv." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Heslo" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Heslo" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL pro Transmission RPC rozhraní" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Uživatelské jméno" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Uživatelské jméno" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Nelze se připojit k klientovi. Prosím aktualizujte nastavení připojení." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Problém s připojením" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Oznámení" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "List Torrentů" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Taxonomie Torrentu" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Historie přenosu dat" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Podrobnosti o rychlosti přenosu dat" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Přidat pravidlo pro stahování" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Přidat kanál" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Použitelný kanál" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Použít štítky" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Procházet kanály" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Ověřte pravidlo vyzkoušením. Neuloženo ani neodesláno." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Vyjmout" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Vyloučit vzor" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Existující kanály" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Stávající pravidla" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Popisek" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Zápas" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " shoduje se s" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " se shoduje s" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Vzor zápasu" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Žádné zdroje nejsou k dispozici." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Nebyly definovány žádné kanály." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Kritériím vyhledávání neodpovídají žádné položky." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Nebyla definována žádná pravidla." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Hledat termín" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Hledaný výraz" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Vybrat kanál" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Spustit při načítání" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Pravidla pro stahování" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Zdroje" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent kanály" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Štítky" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Otestovat srovnávací vzor" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Dny" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Hodiny" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Zápis z jednání" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Cíl Torrentu" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Interval musí být celé, kladné číslo." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Neplatný regulární výraz." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Musíte vybrat kanál." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Musíte zadat destinaci." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Musíte zadat popisek." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Musíte zadat platnou URL kanálu." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Prázdný adresář." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Záplava nemá oprávnění ke čtení tohoto adresáře." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Tato cesta neexistuje. Bude vytvořena." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Došlo k neznámé chybě. Prosím, opakujte akci." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Načítání adresářové struktury..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Nadřazená složka" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Vše" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Aktivní" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Kontrola" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Dokončeno" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Stahování" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Chyba" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Neaktivní" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seedování" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Zastaveno" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtrovat podle stavu" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtrovat podle tagu" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtrovat podle sledovače" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Neoznačeno" + } + ], + "general.ago": [ + { + "type": 0, + "value": "zpět" + } + ], + "general.at": [ + { + "type": 0, + "value": "v" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Zkopírováno" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Kopírovat" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Došlo k neznámé chybě" + } + ], + "general.of": [ + { + "type": 0, + "value": "z" + } + ], + "general.to": [ + { + "type": 0, + "value": "do" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automaticky" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Začít překládat" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Došlo k chybě při běhu mediainfo na serveru. Zkontrolujte, zda je mediainfo nainstalováno a dostupné v PATH na Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Načítání..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo výstup" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Vymazat vše" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Položka kanálu zařazena do fronty" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "Žádné oznámení k zobrazení." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Zobrazení" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Nahlášena chyba" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Dokončené stahování" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Nestahovat" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Vysoká" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Nízká" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normální" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "O záplavách" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Stáhnout sloty globálně" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Stáhnout Slot Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Dostupnost slotu" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Nahrát globální sloty" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Nahrát sloty na Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Přednastavení rozbalovacího seznamu: Stáhnout" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Předvolby rozbalovacího seznamu: Nahrát" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Globální rychlost stahování plynu" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Plyn globální rychlosti nahrávání" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Převodní rychlost tahů" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Povolit DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "Port DHT" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralizované objevení Klienta" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Příchozí připojení" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Nahlášený IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximální HTTP připojení" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Povolit Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Klienti požadováni" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Klienti" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximální počet klientů" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimální počet klientů" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximální počet Distribucí Klientů" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimální počet Distribucí Klientů" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Otevřít port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Náhodné nastavení portu" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Rozsah portu" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Využití přípojných bodů disku" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Zobrazit" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Ověřit hash po dokončení" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Výchozí adresář pro stahování" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximální počet otevřených souborů" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Paměť" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Maximální využití paměti" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "O aplikaci" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Ověření" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Šířka pásma" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Připojení" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Využití disku" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Nastavení" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Zdroje" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Uživatelské rozhraní" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Položky kontextové nabídky" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Detailní torrent sloupce" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Jazyk" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Místní prostředí" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Předvolby výběru štítků" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Výběr více možností" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Výběr jedné možnosti" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Zobrazit" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Povoleno" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "V rozšířeném zobrazení, značky fungují nejlépe na konci seznamu." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Zobrazení seznamu Torrentů" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Velikost Torrentu" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Zkrácené zobrazení" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Rozšířené zobrazení" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Zdroje" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Odhlásit se" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Nastavení" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Omezení rychlosti" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Tmavý motiv" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Světlý motiv" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Hledat torrenty" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "STÁHNOUT" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "NAHRÁT" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Staženo" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Nahráno" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Bez omezení" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Volné" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Využití disku" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Celkem" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Použité" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "z" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Přidat Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Volitelné cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Místo určení" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Místo určení" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Přidat torrenty" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Spustit Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Vytvořit" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "nebo klikněte pro procházení" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Sem přetáhněte některé soubory." + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Podle souboru" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL nebo Magnet odkaz" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Podle URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Štítky" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrenty" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Volitelný základní soubor nebo název adresáře torrentu" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Základní název" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Volitelný komentář v souboru torrentu" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Komentář" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Volitelná zdrojová položka v infohashi" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Zdroj informací" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Soukromé" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Zdroj" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Štítky ve Floodu. Nepřidáno k vytvořenému torrentu." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "URL Trackeru" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackery" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Použít jako základní cestu" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Dokončeno" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pozastavit" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Začít" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Zastavit" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Detaily" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Soubory" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Stáhněte soubor" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Stáhněte soubory" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Načítání detailu souboru..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Komentář" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " připojen k " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Přidáno" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Datum vytvoření" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Staženo" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Volné místo na disku" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Obecná ustanovení" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Sledování" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Převod" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Poloha" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Nic" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Klienti" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Plánovač" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignorováno" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Ovesné" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Semena" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Velikost" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Štítky" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Sledovací zpráva" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Typ" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Soukromé" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Veřejnost" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Klienti" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Pro tento torrent nejsou k dispozici žádná klientská data." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " vybral soubor" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " vybrané soubory" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Nastavit prioritu" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Sledovače" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Pro tento torrent nejsou k dispozici žádná data trackeru." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Typ" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Vygenerovat magnet odkaz" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Načítání trackerů..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet odkaz" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet odkaz s trackery" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "Toto je soukromý torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Nelze se připojit ke klientu." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Vymazat filtry" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Zkontrolovat Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Podrobnosti Torrentu" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Stáhnout" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Vygenerovat magnet odkaz" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Nastavit umístění Torrentu" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pozastavit" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priorita" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Odebrat" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Nastavit štítky" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Nastavit Trackery" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Začít" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Zastavit" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Přetáhněte soubory sem pro přidání." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Žádné torrenty k zobrazení." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Nastavit umístění" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Nastavení..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Zkontrolovat hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Přesunout data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Nastavit umístění Torrentu" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Komentář" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Datum vytvoření" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Přidáno" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Poloha" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Rychlost stahování" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Staženo" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Volné místo na disku" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignorovat plánovač" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Soukromé" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Název" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peery" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Procento dokončeno" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Poměr" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seedy" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Velikost souboru" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Štítky" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Sledovací zpráva" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackery" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Rychlost nahrávání" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Nahráno" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Odebrat Torrenty" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Jste si jisti, že chcete odstranit " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrentů" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Odstranit data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Nevybrali jste žádné torrenty." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Nastavit štítky" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Zadejte štítky" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Nastavit štítky" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Nastavit Trackery" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Vložit tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Nastavit Trackery" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Načítání trackerů..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Seřadit podle" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "CZ" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "hod." + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "tý" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "let" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/da.json b/client/src/javascript/i18n/compiled/da.json new file mode 100644 index 000000000..2d7b3e66d --- /dev/null +++ b/client/src/javascript/i18n/compiled/da.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Tilføj Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Fjern Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Indstillingerne er gemt korrekt." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Tilføjet " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Mislykkedes at tilføje " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Flyttede " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Mislykkedes at flytte " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Slettede " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Mislykkedes at fjerne " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Tilføj Bruger" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Administrator" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Opret Konto" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Opret en konto" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Velkommen til Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Nuværende Bruger" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Adgangskoden må ikke være tom." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Brugernavn må ikke være tomt." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log Ind" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Login" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log ind på din konto." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Brugeren er ikke Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Adgangskode" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Bruger Konti" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Brugernavn" + } + ], + "button.add": [ + { + "type": 0, + "value": "Tilføj" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Annuller" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Hent" + } + ], + "button.new": [ + { + "type": 0, + "value": "Ny" + } + ], + "button.no": [ + { + "type": 0, + "value": "Nej" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Gem Indstillinger" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Gem" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Tilføjer..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Ja" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Kan ikke oprette forbindelse til klienten" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Forbindelsen kunne ikke verificeres." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Klient" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Forbindelsesindstillinger kan ikke være tomme." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Adgangskode" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Adgangskode" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL til qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Brugernavn" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Brugernavn" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Vært" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Værtsnavn eller IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Sti" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Sti til sokkel" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Forbindelsestype" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Kan ikke oprette forbindelse til klienten. Opdater forbindelsesindstillinger." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Tilslutningsproblem" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifikationer" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent Liste" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taksonomi" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Overførselshistorik" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Oplysninger Om Dataoverførselsrate" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Tilføj Download Regel" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Tilføj Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Gældende Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Anvend Tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Gennemse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Udeluk" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Udeluk Mønster" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Eksisterende Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Eksisterende Regler" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Etiket" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Match" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match Mønster" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Ingen tilgængelige feeds." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Ingen feeds defineret." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Ingen elementer, der matcher søgeord." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Ingen regler defineret." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Søgeord" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Søgeord" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Vælg Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start ved indlæsning" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Download Regler" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Mærker" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Mønster" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Dage" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Timer" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutter" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Intervallet skal være et positivt heltal." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Ugyldigt regulært udtryk." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Du skal vælge et feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Du skal angive en destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Du skal angive en etiket." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Du skal angive en gyldig feed-URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Tom mappe." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Oversvømmelse har ikke tilladelse til at læse denne mappe." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Denne sti eksisterer ikke. Den vil blive oprettet." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Der opstod en ukendt fejl. Prøv venligst igen." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Henter mappestruktur..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Overordnet Mappe" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Alle" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Aktiv" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Kontrol" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloader" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Fejl" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inaktiv" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeder" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stoppet" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtrer efter status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtrer efter tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtrer efter Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Utagget" + } + ], + "general.ago": [ + { + "type": 0, + "value": "siden" + } + ], + "general.at": [ + { + "type": 0, + "value": "på" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Kopieret" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Kopiér" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Der opstod en ukendt fejl" + } + ], + "general.of": [ + { + "type": 0, + "value": "af" + } + ], + "general.to": [ + { + "type": 0, + "value": "til" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatisk" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Der opstod en fejl under kørsel af mediainfo på serveren. Kontroller at mediainfo er installeret og tilgængelig i PATH til Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Henter..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Output" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Ryd Alle" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Element I Køen" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Viser" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Fejl Rapporteret" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Download Færdig" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Download Ikke" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Høj" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Lav" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Om Oversvømmelse" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download Slots Globalt" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download Slots Pr. Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Tilgængelighed På Slot" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload Slots Pr. Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown Forudindstillinger: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown Forudindstillinger: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global Downloadhastighed Gas" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global Upload Rate Gas" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Overførselsrate Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Aktiver DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentraliseret Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Indgående Forbindelser" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Rapporteret Ip/Værtsnavn" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maksimum Http-forbindelser" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Aktiver Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Peers Ønsket" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maksimum Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Mindste Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maksimum Peers Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum Peers Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Åbn Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Tilfældig Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Range" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Monteringspunkter For Diskforbrug" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Vis" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verificér Hash ved fuldførelse" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Standard Download Mappe" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maksimum Åbne Filer" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Hukommelse" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Maks. Hukommelsesforbrug" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Om" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Godkendelse" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Båndbredde" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Tilslutning" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Diskforbrug" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Indstillinger" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Ressourcer" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Brugergrænseflade" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Kontekst Menupunkter" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detaljekolonner" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Sprog" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Landestandard" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Vis" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Aktiveret" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "I den udvidede visning fungerer tags bedst i slutningen af listen." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent Liste Visning" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Størrelse" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Kondenseret Visning" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Udvidet Visning" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log Ud" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Indstillinger" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Grænser For Hastighed" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Søg efter torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Downloadet" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uploadet" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Ubegrænset" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Gratis" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Diskforbrug" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "I Alt" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Brugt" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "af" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Tilføj Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Valgfri cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Bestemmelse" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Bestemmelse" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Tilføj Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Opret" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "eller klik for at gennemse" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Slip nogle filer her," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Efter Fil" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL eller Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Efter URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Mærker" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Valgfri basisfil eller mappenavn for torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Basis Navn" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Valgfri kommentar i torrent fil" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Valgfri kildeindtastning i infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Kilde" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Kilde" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags i Flood. Ikke tilføjet til oprettet torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackere" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Brug som basissti" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Afsluttet" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Detaljer" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Filer" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download Fil" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download Filer" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Indlæser fildetaljer..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " forbundet af " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Tilføjet" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Oprettelsesdato" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Downloadet" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Ledig Diskplads" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Generelt" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Overfør" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Placering" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Ingen" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Planlægger" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignoreret" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Afskallet" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Frø" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Størrelse" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Mærker" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker Besked" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Offentlig" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Der er ingen peer data for denne torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " valgt fil" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " valgte filer" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Angiv Prioritet" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackere" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Der er ingen tracker data for denne torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Kan ikke forbinde til klienten." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Ryd Filtre" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Tjek Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Detaljer" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Hent" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Angiv Torrent Placering" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Prioritet" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Fjern" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Sæt Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Indstil Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Slip filer her for at tilføje dem." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Ingen torrents at vise." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Angiv Placering" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Indstillinger..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Tjek hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Flyt data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Angiv Torrent Placering" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Oprettelsesdato" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Tilføjet" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Download Hastighed" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Downloadet" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Ledig Diskplads" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignorer Skemalægning" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Navn" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Procent Fuldført" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Forhold" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Frø" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Fil Størrelse" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Mærker" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker Besked" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackere" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Upload Hastighed" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uploadet" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Fjern Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Er du sikker på, at du vil fjerne " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Slet data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Du har ikke valgt nogen torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Sæt Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Indtast tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Sæt Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Indstil Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Indtast en tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Indstil Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Indlæser trackere..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sortér Efter" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "time" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "uge" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/de.json b/client/src/javascript/i18n/compiled/de.json new file mode 100644 index 000000000..59b372fff --- /dev/null +++ b/client/src/javascript/i18n/compiled/de.json @@ -0,0 +1,2616 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Torrent hinzufügen" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Torrent entfernen" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Torrent starten" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Torrent stoppen" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Einstellungen erfolgreich gespeichert." + } + ], + "alert.torrent.add": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " erfolgreich hinzugefügt." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Hinzufügen von " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " fehlgeschlagen." + } + ], + "alert.torrent.move": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " erfolgreich verschoben." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Verschieben von " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " fehlgeschlagen." + } + ], + "alert.torrent.remove": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " erfolgreich entfernt." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Entfernen von " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " fehlgeschlagen." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Benutzer hinzufügen" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Konto erstellen" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Ein Konto erstellen" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Willkommen bei Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Aktueller Benutzer" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Passwort darf nicht leer sein." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Benutzername darf nicht leer sein." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Löschen" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Anmelden" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Anmelden" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Melden Sie sich bei Ihrem Konto an." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Benutzer ist kein Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Passwort" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Benutzerkonten" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Benutzername" + } + ], + "button.add": [ + { + "type": 0, + "value": "Hinzufügen" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Abbrechen" + } + ], + "button.close": [ + { + "type": 0, + "value": "Schließen" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "Neu" + } + ], + "button.no": [ + { + "type": 0, + "value": "Nein" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Erneut versuchen" + } + ], + "button.save": [ + { + "type": 0, + "value": "Einstellungen speichern" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Speichern" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Hinzufügen..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Ja" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Clientverbindungseinstellungen aktualisieren" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Mit aktuellen Clientverbindungseinstellungen erneut versuchen" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Keine Verbindung zum Client möglich" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Bitte kontaktieren Sie Ihren Flood-Administrator, wenn dies weiterhin geschieht." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Verbindung konnte nicht hergestellt werden." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Verbindungseinstellungen dürfen nicht leer sein." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Passwort" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Passwort" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL zur qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Benutzername" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Benutzername" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname oder IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Pfad" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Pfad zum Socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Verbindungstyp" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Das Bereitstellen von rTorrent via TCP kann zu einer Privilegien-Eskalation führen." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Passwort" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Passwort" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL zur Transmission RPC-Schnittstelle" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Benutzername" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Benutzername" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Keine Verbindung zum Client. Bitte Verbindungseinstellungen aktualisieren." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Verbindungsproblem" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Benachrichtigungen" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent-Liste" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent-Taxonomie" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Datenübertragungsverlauf" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Datenübertragungsrate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Downloadregel hinzufügen" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Feed hinzufügen" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Zutreffender Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Tags anwenden" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Feeds durchsuchen" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Überprüfe die Regel durch ausprobieren. Wird weder gespeichert noch gesendet." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Ausschließen" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Muster ausschließen" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Vorhandene Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Vorhandene Regeln" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Intervall" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Label" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Übereinstimmung" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " Übereinstimmung" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " Übereinstimmungen" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match-Muster" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Keine Feeds verfügbar." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Keine Feeds definiert." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Keine Artikel stimmen mit dem Suchbegriff überein." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Keine Regeln definiert." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Suchbegriff" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Suchbegriff" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Feed auswählen" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Intervall" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Beim Abrufen starten" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Downloadregeln" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent-Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test-Match-Muster" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Tage" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Stunden" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minuten" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Downloadpfad des Torrents" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Das Intervall muss eine positive Ganzzahl sein." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Ungültiger regulärer Ausdruck." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Sie müssen einen Feed auswählen." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Sie müssen einen Downloadpfad angeben." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Sie müssen ein Label angeben." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Sie müssen eine gültige Feed-URL angeben." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Leeres Verzeichnis." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood hat keine Berechtigung, dieses Verzeichnis zu lesen." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Dieser Pfad existiert nicht. Er wird erstellt." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Ein unbekannter Fehler ist aufgetreten. Bitte erneut versuchen." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Lade Verzeichnisstruktur..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Übergeordnetes Verzeichnis" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Alle" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Aktiv" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Prüfe" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Abgeschlossen" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Beim Herunterladen" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Fehlerhaft" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inaktiv" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seede" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Gestoppt" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Nach Status filtern" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Nach Tag filtern" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Nach Tracker filtern" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Ohne Tag" + } + ], + "general.ago": [ + { + "type": 0, + "value": "vor" + } + ], + "general.at": [ + { + "type": 0, + "value": "am" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Kopiert" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Kopieren" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Ein unbekannter Fehler ist aufgetreten" + } + ], + "general.of": [ + { + "type": 0, + "value": "von" + } + ], + "general.to": [ + { + "type": 0, + "value": "zu" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatisch" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Übersetzung starten" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Beim Ausführen von mediainfo auf dem Server ist ein Fehler aufgetreten. Überprüfen Sie, ob mediainfo installiert ist und im PATH to Flood verfügbar ist." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Abrufen..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Ausgabe von Mediainfo" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Alle löschen" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Artikel wurde hinzugefügt" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "Keine anzuzeigenden Benachrichtigungen." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Anzeigen" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Fehler gemeldet" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Download abgeschlossen" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Nicht herunterladen" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Hoch" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Niedrig" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Über Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Maximale globale Anzahl von DL-Slots" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Maximale Anzahl von DL-Slots pro Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slot-Beschränkungen" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Maximale globale Anzahl von UL-Slots" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Maximale Anzahl von UL-Slots pro Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown-Voreinstellungen: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown-Voreinstellungen: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Begrenzung der globalen DL-Rate" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Begrenzung der globalen UL-Rate" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Verbindungsbeschränkungen" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "DHT aktivieren" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Dezentralisiertes Netzwerk" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Eingehende Verbindungen" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Gemeldete/r IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximale Anzahl von HTTP-Verbindungen" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Peer-Exchange aktivieren" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Gewünschte Anzahl von Peers" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximale Anzahl von Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimale Anzahl von Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximale Anzahl von Peers für Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimale Anzahl von Peers für Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Offener Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Zufälliger Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port-Bereich" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Einhängepunkte für Speichernutzung" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Anzeigen" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Hash nach Abschluss verifizieren" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Standard-Download-Verzeichnis" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Speicher" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximal geöffnete Dateien" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Arbeitsspeicher" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Maximale Speichernutzung" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Über" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentifizierung" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Geschwindigkeit" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Verbindung" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Speichernutzung" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Einstellungen" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Ressourcen" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Benutzeroberfläche" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Einträge des Kontextmenüs" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Detailspalten der Torrent-Liste" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Sprache" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Lokalisierung" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Bevorzugte Tag-Auswahl" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Mehrfachauswahl" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Einzelauswahl" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Anzeigen" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Aktiv" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In der erweiterten Ansicht funktionieren Tags am Ende der Liste am besten." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Anzeige der Torrent-Liste" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent-Größe" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Kompaktes Layout" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Erweitertes Layout" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Abmelden" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Einstellungen" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Geschwindigkeitslimits" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dunkles Design" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Helles Design" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Suche Torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Heruntergeladen" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Hochgeladen" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Unbegrenzt" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Frei" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Speichernutzung" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Gesamt" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Belegt" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "von" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Torrent hinzufügen" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional Cookie-Name=Cookie-Wert" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Downloadpfad" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Downloadpfad" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Torrents hinzufügen" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Torrent starten" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Erstellen" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "oder klicken, um zu durchsuchen" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Dateien hier ablegen" + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Von Datei" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent-URL oder Magnet-Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Von URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optionale Basisdatei oder Verzeichnisname des Torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Basisname" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optionaler Kommentar in Torrent-Datei" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optionaler Quelleintrag in Infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Informationsquelle" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Quelle" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags für Flood. Wird dem erstellten Torrent nicht hinzugefügt." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker-URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Als Basispfad verwenden" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Abgeschlossen" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Details" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Dateien" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Datei herunterladen" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Dateien herunterladen" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Lade Dateidetails..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " verbunden von " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Hinzugefügt" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Erstellungszeit" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Heruntergeladen" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Freier Speicherplatz" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hashwert" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Allgemein" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Übertragungen" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Speicherort" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Keine" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Zeitplaner" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignoriert" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Befolgt" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Größe" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker-Meldung" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Typ" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Öffentlich" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Es gibt keine Peer-Daten für diesen Torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " Datei ausgewählt" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " Dateien ausgewählt" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Priorität setzen" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Es gibt keine Tracker-Daten für diesen Torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Typ" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Magnet-Link generieren" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Lade Tracker..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet-Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet-Link mit Trackern" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "Dies ist ein privates Torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Keine Verbindung zum Client möglich." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Filter zurücksetzen" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Hash prüfen" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent-Details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Magnet-Link generieren" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Speicherort setzen" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pausieren" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priorität" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Entfernen" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Tags setzen" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Tracker festlegen" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stoppen" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Dateien hier ablegen, um sie hinzuzufügen." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Keine anzuzeigenden Torrents." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Speicherort setzen" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Einstellung..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Hash prüfen" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Daten verschieben" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Speicherort des Torrents setzen" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Erstellungsdatum" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Hinzugefügt" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Speicherort" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "DL-Geschwindigkeit" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Heruntergeladen" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "Fertig in" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Freier Speicherplatz" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Terminplaner ignorieren" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Name" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Fortschritt" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Verhältnis" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Größe" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker-Meldung" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "UL-Geschwindigkeit" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Hochgeladen" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Torrents entfernen" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Wollen Sie wirklich " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " Torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " entfernen?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Daten löschen" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Sie haben keine Torrents ausgewählt." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Tags zuweisen" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Tags eingeben" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Tags zuweisen" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Tracker festlegen" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Tracker eingeben" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Tracker festlegen" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Lade Tracker..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sortieren nach" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "h" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/el.json b/client/src/javascript/i18n/compiled/el.json new file mode 100644 index 000000000..876af72fd --- /dev/null +++ b/client/src/javascript/i18n/compiled/el.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Remove Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Successfully saved settings." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Successfully added " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Failed to add " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Successfully moved " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Failed to move " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Successfully removed " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Failed to remove " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Add User" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Create Account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Create an account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Welcome to Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Current User" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Username cannot be empty." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log In" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Login" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in to your account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "User is not Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Password" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "User Accounts" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Username" + } + ], + "button.add": [ + { + "type": 0, + "value": "Add" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancel" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "New" + } + ], + "button.no": [ + { + "type": 0, + "value": "No" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Save Settings" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Save" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Adding..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Yes" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Connection could not be verified." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Connectivity Issue" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent List" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Transfer History" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Add Download Rule" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Add Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Apply Tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Browse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclude" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exclude Pattern" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Existing Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Existing Rules" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Label" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Match" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matches" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match Pattern" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "No feeds available." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "No feeds defined." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No items matching search term." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "No rules defined." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Select Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start on load" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Download Rules" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Days" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Hours" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutes" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "The interval must be a positive integer." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Invalid regular expression." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "You must select a feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "You must specify a destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "You must specify a label." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "You must specify a valid feed URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Empty directory." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood does not have permission to read this directory." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "This path does not exist. It will be created." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Fetching directory structure..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Parent Directory" + } + ], + "filter.all": [ + { + "type": 0, + "value": "All" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Active" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Checking" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloading" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Error" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactive" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stopped" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filter by Status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filter by Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filter by Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Untagged" + } + ], + "general.ago": [ + { + "type": 0, + "value": "ago" + } + ], + "general.at": [ + { + "type": 0, + "value": "at" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copied" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copy" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "of" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatic" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Fetching..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Output" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Clear All" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Showing" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Error Reported" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Finished Downloading" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Don't Download" + } + ], + "priority.high": [ + { + "type": 0, + "value": "High" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Low" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "About Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download Slots Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slot Availability" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload Slots Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown Presets: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown Presets: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global Download Rate Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global Upload Rate Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Transfer Rate Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Enable DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralized Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Incoming Connections" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Reported IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximum HTTP Connections" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Enable Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Peers Desired" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximum Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimum Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximum Peers Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum Peers Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Open Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Randomize Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Range" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verify Hash on Completion" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Default Download Directory" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximum Open Files" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memory" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Max Memory Usage" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentication" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandwidth" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectivity" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Settings" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resources" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "User Interface" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Language" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Locale" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Enabled" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In the expanded view, tags work best at the end of the list." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent List Display" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Size" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Condensed View" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Expanded View" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log Out" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Settings" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Speed Limits" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Search torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Unlimited" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Free" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Used" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "of" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Add Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "or click to browse" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Drop some files here," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "By File" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL or Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "By URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Use as Base Path" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Details" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Files" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download File" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download Files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Loading file detail..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connected of " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "General" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfer" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "None" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Scheduler" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignored" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Size" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "There is no peer data for this torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected file" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Set Priority" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "There is no tracker data for this torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Clear Filters" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Check Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priority" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Remove" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "No torrents to display." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Set Location" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Setting..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Check hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Move data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Download Speed" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignore Scheduler" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Name" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percent Complete" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "File Size" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Upload Speed" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Remove Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Are you sure you want to remove " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Delete data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "You haven't selected any torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Enter tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sort By" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "hr" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/en.json b/client/src/javascript/i18n/compiled/en.json new file mode 100644 index 000000000..68480d2e0 --- /dev/null +++ b/client/src/javascript/i18n/compiled/en.json @@ -0,0 +1,2492 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "crwdns2902:0crwdne2902:0" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "crwdns2904:0crwdne2904:0" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "crwdns2898:0crwdne2898:0" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "crwdns2900:0crwdne2900:0" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "crwdns2918:0crwdne2918:0" + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "crwdns2906:0countElement=" + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": "crwdnd2906:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdne2906:0" + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "crwdns2908:0countElement=" + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": "crwdnd2908:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdne2908:0" + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "crwdns2910:0countElement=" + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": "crwdnd2910:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdne2910:0" + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "crwdns2912:0countElement=" + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": "crwdnd2912:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdne2912:0" + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "crwdns2914:0countElement=" + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": "crwdnd2914:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdne2914:0" + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "crwdns2916:0countElement=" + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": "crwdnd2916:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdne2916:0" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "crwdns2920:0crwdne2920:0" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "crwdns2950:0crwdne2950:0" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "crwdns2928:0crwdne2928:0" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "crwdns2930:0crwdne2930:0" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "crwdns2932:0crwdne2932:0" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "crwdns2934:0crwdne2934:0" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "crwdns3548:0crwdne3548:0" + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "crwdns2936:0crwdne2936:0" + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "crwdns3648:0crwdne3648:0" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "crwdns2938:0crwdne2938:0" + } + ], + "auth.login": [ + { + "type": 0, + "value": "crwdns2940:0crwdne2940:0" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "crwdns2942:0crwdne2942:0" + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "crwdns2952:0crwdne2952:0" + } + ], + "auth.password": [ + { + "type": 0, + "value": "crwdns2944:0crwdne2944:0" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "crwdns2946:0crwdne2946:0" + } + ], + "auth.username": [ + { + "type": 0, + "value": "crwdns2948:0crwdne2948:0" + } + ], + "button.add": [ + { + "type": 0, + "value": "crwdns2962:0crwdne2962:0" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "crwdns2964:0crwdne2964:0" + } + ], + "button.close": [ + { + "type": 0, + "value": "crwdns3692:0crwdne3692:0" + } + ], + "button.download": [ + { + "type": 0, + "value": "crwdns3520:0crwdne3520:0" + } + ], + "button.new": [ + { + "type": 0, + "value": "crwdns2978:0crwdne2978:0" + } + ], + "button.no": [ + { + "type": 0, + "value": "crwdns2966:0crwdne2966:0" + } + ], + "button.ok": [ + { + "type": 0, + "value": "crwdns3680:0crwdne3680:0" + } + ], + "button.retry": [ + { + "type": 0, + "value": "crwdns3654:0crwdne3654:0" + } + ], + "button.save": [ + { + "type": 0, + "value": "crwdns2968:0crwdne2968:0" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "crwdns2970:0crwdne2970:0" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "crwdns2974:0crwdne2974:0" + } + ], + "button.yes": [ + { + "type": 0, + "value": "crwdns2976:0crwdne2976:0" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "crwdns3658:0crwdne3658:0" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "crwdns3656:0crwdne3656:0" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "crwdns3580:0crwdne3580:0" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "crwdns3660:0crwdne3660:0" + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "crwdns3492:0crwdne3492:0" + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "crwdns3550:0crwdne3550:0" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "crwdns3552:0crwdne3552:0" + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "crwdns3630:0crwdne3630:0" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "crwdns3640:0crwdne3640:0" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "crwdns3642:0crwdne3642:0" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "crwdns3632:0crwdne3632:0" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "crwdns3634:0crwdne3634:0" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "crwdns3636:0crwdne3636:0" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "crwdns3638:0crwdne3638:0" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "crwdns3554:0crwdne3554:0" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "crwdns3562:0crwdne3562:0" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "crwdns3564:0crwdne3564:0" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "crwdns3566:0crwdne3566:0" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "crwdns3568:0crwdne3568:0" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "crwdns3570:0crwdne3570:0" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "crwdns3572:0crwdne3572:0" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "crwdns3556:0crwdne3556:0" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "crwdns3560:0crwdne3560:0" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "crwdns3558:0crwdne3558:0" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "crwdns3652:0crwdne3652:0" + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "crwdns3666:0crwdne3666:0" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "crwdns3676:0crwdne3676:0" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "crwdns3678:0crwdne3678:0" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "crwdns3668:0crwdne3668:0" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "crwdns3670:0crwdne3670:0" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "crwdns3672:0crwdne3672:0" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "crwdns3674:0crwdne3674:0" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "crwdns3574:0crwdne3574:0" + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "crwdns2980:0crwdne2980:0" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "crwdns3502:0crwdne3502:0" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "crwdns3510:0crwdne3510:0" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "crwdns3504:0crwdne3504:0" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "crwdns3508:0crwdne3508:0" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "crwdns3506:0crwdne3506:0" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "crwdns2984:0crwdne2984:0" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "crwdns2986:0crwdne2986:0" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "crwdns2988:0crwdne2988:0" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "crwdns2990:0crwdne2990:0" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "crwdns3054:0crwdne3054:0" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "crwdns3650:0crwdne3650:0" + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "crwdns3006:0crwdne3006:0" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "crwdns2992:0crwdne2992:0" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "crwdns2994:0crwdne2994:0" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "crwdns2996:0crwdne2996:0" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "crwdns3524:0crwdne3524:0" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "crwdns2998:0crwdne2998:0" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "crwdns3004:0crwdne3004:0" + } + ], + "feeds.match.count": [ + { + "type": 0, + "value": "crwdns3000:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdne3000:0" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "crwdns3002:0crwdne3002:0" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "crwdns3008:0crwdne3008:0" + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "crwdns3010:0crwdne3010:0" + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "crwdns3526:0crwdne3526:0" + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "crwdns3012:0crwdne3012:0" + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "crwdns3014:0crwdne3014:0" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "crwdns3040:0crwdne3040:0" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "crwdns3530:0crwdne3530:0" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "crwdns3016:0crwdne3016:0" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "crwdns3018:0crwdne3018:0" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "crwdns3020:0crwdne3020:0" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "crwdns3022:0crwdne3022:0" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "crwdns3024:0crwdne3024:0" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "crwdns3026:0crwdne3026:0" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "crwdns3028:0crwdne3028:0" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "crwdns3528:0crwdne3528:0" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "crwdns3034:0crwdne3034:0" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "crwdns3030:0crwdne3030:0" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "crwdns3032:0crwdne3032:0" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "crwdns3036:0crwdne3036:0" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "crwdns3038:0crwdne3038:0" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "crwdns3052:0crwdne3052:0" + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "crwdns3042:0crwdne3042:0" + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "crwdns3044:0crwdne3044:0" + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "crwdns3046:0crwdne3046:0" + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "crwdns3048:0crwdne3048:0" + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "crwdns3050:0crwdne3050:0" + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "crwdns3056:0crwdne3056:0" + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "crwdns3058:0crwdne3058:0" + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "crwdns3060:0crwdne3060:0" + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "crwdns3606:0crwdne3606:0" + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "crwdns3062:0crwdne3062:0" + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "crwdns3518:0crwdne3518:0" + } + ], + "filter.all": [ + { + "type": 0, + "value": "crwdns3064:0crwdne3064:0" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "crwdns3072:0crwdne3072:0" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "crwdns3080:0crwdne3080:0" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "crwdns3070:0crwdne3070:0" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "crwdns3068:0crwdne3068:0" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "crwdns3076:0crwdne3076:0" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "crwdns3074:0crwdne3074:0" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "crwdns3546:0crwdne3546:0" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "crwdns3078:0crwdne3078:0" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "crwdns3066:0crwdne3066:0" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "crwdns3084:0crwdne3084:0" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "crwdns3082:0crwdne3082:0" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "crwdns3086:0crwdne3086:0" + } + ], + "general.ago": [ + { + "type": 0, + "value": "crwdns3088:0crwdne3088:0" + } + ], + "general.at": [ + { + "type": 0, + "value": "crwdns3090:0crwdne3090:0" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "crwdns3098:0crwdne3098:0" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "crwdns3096:0crwdne3096:0" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "crwdns3614:0crwdne3614:0" + } + ], + "general.of": [ + { + "type": 0, + "value": "crwdns3094:0crwdne3094:0" + } + ], + "general.to": [ + { + "type": 0, + "value": "crwdns3092:0crwdne3092:0" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "crwdns3498:0crwdne3498:0" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "crwdns3646:0crwdne3646:0" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "crwdns3100:0crwdne3100:0" + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "crwdns3102:0crwdne3102:0" + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "crwdns3104:0crwdne3104:0" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "crwdns3114:0crwdne3114:0" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 0, + "value": "crwdns3618:0" + }, + { + "type": 1, + "value": "title" + }, + { + "type": 0, + "value": "crwdne3618:0" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "crwdns3616:0crwdne3616:0" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "crwdns3662:0crwdne3662:0" + } + ], + "notification.showing": [ + { + "type": 0, + "value": "crwdns3116:0crwdne3116:0" + } + ], + "notification.torrent.errored.body": [ + { + "type": 0, + "value": "crwdns3112:0" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "crwdne3112:0" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "crwdns3110:0crwdne3110:0" + } + ], + "notification.torrent.finished.body": [ + { + "type": 0, + "value": "crwdns3108:0" + }, + { + "type": 1, + "value": "name" + }, + { + "type": 0, + "value": "crwdne3108:0" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "crwdns3106:0crwdne3106:0" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "crwdns3118:0crwdne3118:0" + } + ], + "priority.high": [ + { + "type": 0, + "value": "crwdns3120:0crwdne3120:0" + } + ], + "priority.low": [ + { + "type": 0, + "value": "crwdns3122:0crwdne3122:0" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "crwdns3124:0crwdne3124:0" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "crwdns3232:0crwdne3232:0" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "crwdns3128:0crwdne3128:0" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "crwdns3130:0crwdne3130:0" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "crwdns3132:0crwdne3132:0" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "crwdns3136:0crwdne3136:0" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "crwdns3138:0crwdne3138:0" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "crwdns3140:0crwdne3140:0" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "crwdns3142:0crwdne3142:0" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "crwdns3144:0crwdne3144:0" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "crwdns3146:0crwdne3146:0" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "crwdns3148:0crwdne3148:0" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "crwdns3150:0crwdne3150:0" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "crwdns3152:0crwdne3152:0" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "crwdns3154:0crwdne3154:0" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "crwdns3156:0crwdne3156:0" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "crwdns3158:0crwdne3158:0" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "crwdns3160:0crwdne3160:0" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "crwdns3162:0crwdne3162:0" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "crwdns3164:0crwdne3164:0" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "crwdns3166:0crwdne3166:0" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "crwdns3168:0crwdne3168:0" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "crwdns3170:0crwdne3170:0" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "crwdns3172:0crwdne3172:0" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "crwdns3174:0crwdne3174:0" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "crwdns3176:0crwdne3176:0" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "crwdns3178:0crwdne3178:0" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "crwdns3180:0crwdne3180:0" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "crwdns3230:0crwdne3230:0" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "crwdns3228:0crwdne3228:0" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "crwdns3182:0crwdne3182:0" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "crwdns3184:0crwdne3184:0" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "crwdns3186:0crwdne3186:0" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "crwdns3188:0crwdne3188:0" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "crwdns3190:0crwdne3190:0" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "crwdns3192:0crwdne3192:0" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "crwdns3208:0crwdne3208:0" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "crwdns3202:0crwdne3202:0" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "crwdns3194:0crwdne3194:0" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "crwdns3196:0crwdne3196:0" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "crwdns3206:0crwdne3206:0" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "crwdns3198:0crwdne3198:0" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "crwdns3200:0crwdne3200:0" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "crwdns3204:0crwdne3204:0" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "crwdns3534:0crwdne3534:0" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "crwdns3226:0crwdne3226:0" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "crwdns3212:0crwdne3212:0" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "crwdns3210:0crwdne3210:0" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "crwdns3682:0crwdne3682:0" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "crwdns3686:0crwdne3686:0" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "crwdns3684:0crwdne3684:0" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "crwdns3532:0crwdne3532:0" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "crwdns3222:0crwdne3222:0" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "crwdns3224:0crwdne3224:0" + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "crwdns3214:0crwdne3214:0" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "crwdns3216:0crwdne3216:0" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "crwdns3220:0crwdne3220:0" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "crwdns3218:0crwdne3218:0" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "crwdns3234:0crwdne3234:0" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "crwdns3242:0crwdne3242:0" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "crwdns3238:0crwdne3238:0" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "crwdns3240:0crwdne3240:0" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "crwdns3688:0crwdne3688:0" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "crwdns3690:0crwdne3690:0" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "crwdns3244:0crwdne3244:0" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "crwdns3250:0crwdne3250:0" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "crwdns3252:0crwdne3252:0" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "crwdns3246:0crwdne3246:0" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "crwdns3248:0crwdne3248:0" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "crwdns3254:0crwdne3254:0" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "crwdns3514:0crwdne3514:0" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "crwdns3496:0crwdne3496:0" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "crwdns3516:0crwdne3516:0" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "crwdns3512:0crwdne3512:0" + } + ], + "torrent.list.peers": [ + { + "type": 0, + "value": "crwdns3414:0" + }, + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": "crwdnd3414:0" + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": "crwdnd3414:0" + }, + { + "type": 1, + "value": "total" + }, + { + "type": 0, + "value": "crwdne3414:0" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "crwdns3416:0crwdne3416:0" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "crwdns3282:0crwdne3282:0" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "crwdns3610:0crwdne3610:0" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "crwdns3608:0crwdne3608:0" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "crwdns3284:0crwdne3284:0" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "crwdns3286:0crwdne3286:0" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "crwdns3288:0crwdne3288:0" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "crwdns3290:0crwdne3290:0" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "crwdns3582:0crwdne3582:0" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "crwdns3292:0crwdne3292:0" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "crwdns3294:0crwdne3294:0" + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "crwdns3296:0crwdne3296:0" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "crwdns3298:0crwdne3298:0" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "crwdns3706:0crwdne3706:0" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "crwdns3300:0crwdne3300:0" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "crwdns3304:0crwdne3304:0" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "crwdns3302:0crwdne3302:0" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "crwdns3592:0crwdne3592:0" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "crwdns3590:0crwdne3590:0" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "crwdns3596:0crwdne3596:0" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "crwdns3594:0crwdne3594:0" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "crwdns3600:0crwdne3600:0" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "crwdns3598:0crwdne3598:0" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "crwdns3602:0crwdne3602:0" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "crwdns3584:0crwdne3584:0" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "crwdns3604:0crwdne3604:0" + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "crwdns3588:0crwdne3588:0" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "crwdns3586:0crwdne3586:0" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "crwdns3306:0crwdne3306:0" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "crwdns3612:0crwdne3612:0" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "crwdns3308:0crwdne3308:0" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "crwdns3310:0crwdne3310:0" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "crwdns3312:0crwdne3312:0" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "crwdns3314:0crwdne3314:0" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "crwdns3316:0crwdne3316:0" + } + ], + "torrents.details.files.download.file": [ + { + "type": 0, + "value": "crwdns3320:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdne3320:0" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "crwdns3318:0crwdne3318:0" + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "crwdns3322:0crwdne3322:0" + } + ], + "torrents.details.general.connected": [ + { + "type": 0, + "value": "crwdns3324:0" + }, + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": "crwdnd3324:0" + }, + { + "type": 1, + "value": "total" + }, + { + "type": 0, + "value": "crwdne3324:0" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "crwdns3326:0crwdne3326:0" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "crwdns3328:0crwdne3328:0" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "crwdns3330:0crwdne3330:0" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "crwdns3332:0crwdne3332:0" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "crwdns3334:0crwdne3334:0" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "crwdns3336:0crwdne3336:0" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "crwdns3338:0crwdne3338:0" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "crwdns3340:0crwdne3340:0" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "crwdns3342:0crwdne3342:0" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "crwdns3344:0crwdne3344:0" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "crwdns3346:0crwdne3346:0" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "crwdns3348:0crwdne3348:0" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "crwdns3354:0crwdne3354:0" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "crwdns3350:0crwdne3350:0" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "crwdns3352:0crwdne3352:0" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "crwdns3356:0crwdne3356:0" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "crwdns3358:0crwdne3358:0" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "crwdns3360:0crwdne3360:0" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "crwdns3362:0crwdne3362:0" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "crwdns3368:0crwdne3368:0" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "crwdns3364:0crwdne3364:0" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "crwdns3366:0crwdne3366:0" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "crwdns3370:0crwdne3370:0" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "crwdns3374:0crwdne3374:0" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "crwdns3372:0crwdne3372:0" + } + ], + "torrents.details.selected.files": [ + { + "type": 0, + "value": "crwdns3376:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdnd3376:0countElement=" + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": "crwdnd3376:0countElement=" + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": "crwdne3376:0" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "crwdns3378:0crwdne3378:0" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "crwdns3384:0crwdne3384:0" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "crwdns3380:0crwdne3380:0" + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "crwdns3382:0crwdne3382:0" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "crwdns3694:0crwdne3694:0" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "crwdns3696:0crwdne3696:0" + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "crwdns3700:0crwdne3700:0" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "crwdns3702:0crwdne3702:0" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "crwdns3698:0crwdne3698:0" + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "crwdns3578:0crwdne3578:0" + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "crwdns3386:0crwdne3386:0" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "crwdns3388:0crwdne3388:0" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "crwdns3390:0crwdne3390:0" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "crwdns3396:0crwdne3396:0" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "crwdns3704:0crwdne3704:0" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "crwdns3392:0crwdne3392:0" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "crwdns3394:0crwdne3394:0" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "crwdns3398:0crwdne3398:0" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "crwdns3400:0crwdne3400:0" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "crwdns3402:0crwdne3402:0" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "crwdns3620:0crwdne3620:0" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "crwdns3404:0crwdne3404:0" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "crwdns3406:0crwdne3406:0" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "crwdns3576:0crwdne3576:0" + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "crwdns3408:0crwdne3408:0" + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "crwdns3418:0crwdne3418:0" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "crwdns3420:0crwdne3420:0" + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "crwdns3424:0crwdne3424:0" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "crwdns3422:0crwdne3422:0" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "crwdns3426:0crwdne3426:0" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "crwdns3432:0crwdne3432:0" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "crwdns3434:0crwdne3434:0" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "crwdns3428:0crwdne3428:0" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "crwdns3664:0crwdne3664:0" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "crwdns3436:0crwdne3436:0" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "crwdns3438:0crwdne3438:0" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "crwdns3440:0crwdne3440:0" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "crwdns3442:0crwdne3442:0" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "crwdns3444:0crwdne3444:0" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "crwdns3446:0crwdne3446:0" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "crwdns3448:0crwdne3448:0" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "crwdns3450:0crwdne3450:0" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "crwdns3468:0crwdne3468:0" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "crwdns3452:0crwdne3452:0" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "crwdns3454:0crwdne3454:0" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "crwdns3466:0crwdne3466:0" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "crwdns3456:0crwdne3456:0" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "crwdns3458:0crwdne3458:0" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "crwdns3460:0crwdne3460:0" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "crwdns3470:0crwdne3470:0" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "crwdns3462:0crwdne3462:0" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "crwdns3464:0crwdne3464:0" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "crwdns3478:0crwdne3478:0" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "crwdns3472:0count=" + }, + { + "type": 1, + "value": "count" + }, + { + "type": 0, + "value": "crwdne3472:0" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "crwdns3474:0crwdne3474:0" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "crwdns3476:0crwdne3476:0" + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "crwdns3480:0crwdne3480:0" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "crwdns3484:0crwdne3484:0" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "crwdns3482:0crwdne3482:0" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "crwdns3622:0crwdne3622:0" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "crwdns3626:0crwdne3626:0" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "crwdns3624:0crwdne3624:0" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "crwdns3628:0crwdne3628:0" + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "crwdns3486:0crwdne3486:0" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "crwdns3256:0crwdne3256:0" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "crwdns3262:0crwdne3262:0" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "crwdns3258:0crwdne3258:0" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "crwdns3260:0crwdne3260:0" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "crwdns3264:0crwdne3264:0" + } + ], + "unit.speed": [ + { + "type": 0, + "value": "crwdns3266:0" + }, + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "crwdne3266:0" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "crwdns3272:0crwdne3272:0" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "crwdns3274:0crwdne3274:0" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "crwdns3280:0crwdne3280:0" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "crwdns3276:0crwdne3276:0" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "crwdns3278:0crwdne3278:0" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "crwdns3270:0crwdne3270:0" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "crwdns3268:0crwdne3268:0" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/es.json b/client/src/javascript/i18n/compiled/es.json new file mode 100644 index 000000000..9084777a1 --- /dev/null +++ b/client/src/javascript/i18n/compiled/es.json @@ -0,0 +1,2617 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Añadir Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Eliminar Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Iniciar Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Detener Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Configuración Guardada." + } + ], + "alert.torrent.add": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent agregado" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents agregados" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent fallo al agregar" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents fallaron al agregarse" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent se ha movido" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents se han movido" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent fallo al moverse" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents fallaron al moverse" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent se ha eliminado" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents se han eliminado" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent fallo al eliminarse" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents fallaron al eliminarse" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Agregar usuario" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Crear Cuenta" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Crear una Cuenta" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "¡Bienvenido a Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Usuario actual" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "La contraseña no puede estar vacía." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Nombre de usuario no puede estar en blanco." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Limpiar" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Iniciar Sesión" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Acceder" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Inicie sesión en su cuenta." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "El Usuario no es Administrador" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Contraseña" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Cuentas de Usuario" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Nombre de Usuario" + } + ], + "button.add": [ + { + "type": 0, + "value": "Agregar" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancelar" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Descargar" + } + ], + "button.new": [ + { + "type": 0, + "value": "Nuevo" + } + ], + "button.no": [ + { + "type": 0, + "value": "Nu" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Reintenta" + } + ], + "button.save": [ + { + "type": 0, + "value": "Guardar Configuración" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Guardar" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Agregando..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Sí" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Actualizar configuración de conexión de cliente" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Reintentar con la configuración de conexión actual del cliente" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "No se puede conectar al cliente" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Por favor contacte a su administrador de Flood si esto continúa." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "No se pudo verificar la conexión." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Cliente" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Los ajustes de conexión no pueden estar vacíos." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Contraseña" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Contraseña" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL a la API web qBittorrent" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Usuario" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Usuario" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rótula" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Anfitrión" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Nombre de host o IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Puerto" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Puerto" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Ruta" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Ruta al socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Tipo de conexión" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exponer rTorrent vía TCP puede permitir la escalada de privilegios." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Contraseña" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Contraseña" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL a la interfaz RPC de Transmission" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Nombre de Usuario" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Nombre de Usuario" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "No se puede conectar al cliente. Por favor, actualice la configuración de conexión." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Problema de conectividad" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notificaciones" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Lista de torrents" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Taxonomía torrente" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Historial de transferencia de datos" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Detalles de tasa de transferencia de datos" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Agregar Regla de Descarga" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Agregar Fuente" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Fuente Correspondiente" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Aplicar Etiquetas" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Ver feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validar la regla probándola. No guardado o enviado." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Excluir" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Patrón de Exclusión" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Fuentes Existentes" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Reglas Existentes" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Intervalo" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Rótulo" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Encontrado" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " Encontrado" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " Encontrados" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Patrón de Inclusión" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "No hay fuentes disponibles" + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "No hay fuentes definidas" + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No hay elementos que coincidan con el término de búsqueda." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "No hay reglas definidas" + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "Regular" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Buscar término" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Buscar término" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Seleccionar Fuente" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Intervalo" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Iniciar al Cargar" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Reglas de Descarga" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Fuentes" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Fuentes de Torrent" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Etiquetas" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Prueba de Patrón de Coincidencia" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Días" + } + ], + "feeds.time.hr": [ + { + "type": 1, + "value": "durationValue" + }, + { + "type": 0, + "value": " hr" + } + ], + "feeds.time.min": [ + { + "type": 1, + "value": "durationValue" + }, + { + "type": 0, + "value": " min" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Destinación de Torrent" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "El intervalo debe ser un entero positivo." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "RegEx invalida." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Debe seleccionar una fuente" + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Debe seleccionar una destinación." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Debe seleccionar un rotulo" + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Debe seleccionar un URL de fuente valido." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Carpeta vacía" + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood no tiene permisos para leer esta carpeta." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Esta ruta no existe. Se creara." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Se ha producido un error desconocido. Por favor, inténtelo de nuevo." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Cargando estructura de carpeta..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Directorio padre" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Todo" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Activo" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Comprobando" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Completo" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Descargando" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Error" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactivo" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Sembrando" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Detenido" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtrar por Estado" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtrar por Etiqueta" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtrar por Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Sin Etiqueta" + } + ], + "general.ago": [ + { + "type": 0, + "value": "hace" + } + ], + "general.at": [ + { + "type": 0, + "value": "en" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copiado" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copiar" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Se ha producido un error desconocido" + } + ], + "general.of": [ + { + "type": 0, + "value": "de" + } + ], + "general.to": [ + { + "type": 0, + "value": "hasta" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automátic" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Empieza a traducir" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Se ha encontrado un error al correr Mediainfo. Confirme que Mediainfo este instalado y disponible a Flood en el PATH" + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Obteniendo..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Salida de Mediainfo" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Borrar Todos" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Suministro de artículo en cola" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "Ninguna notificación para mostrar." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Mostrando" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Error Reportado" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Descarga Completada" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "No Descargar" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Alta" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Baja" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Acerca de Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Cant. Global de Espacios de Descargas" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Cant. por Torrent de Espacios de Descarga" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Disponibilidad de Espacios" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Cant. Global de Espacios de Subida" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Cant. por Torrent de Espacios de Descarga" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Predeterminados de Desplegable: Descarga" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Predeterminados de Desplegable: Subida" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Regulador Global de Velocidad de Descarga" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Regulador Global de Velocidad de Subida" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Reguladores de Velocidad" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Habilitar DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "Puerto para DHT" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Descubrimiento de Peers Descentralizado" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Conexiones Entrantes" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "IP/Hostname Presentado" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Máxima Cant. de Conexiones HTTP" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Habilitar Intercambio de Peers" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Cant. de Peers Deseada" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Pares" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Cant. Máxima de Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Cant. Mínima de Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Cant. Máxima de Peers en Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Cant. Mínima de Peers en Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Puerto Abierto" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Puerto Aleatorio" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Rango de Puertos" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Puntos de Montura de Uso de Disco" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Mostrar" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verificar Hash al Terminar" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Carpeta de Descargas Predeterminada" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disco" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Cant. Máxima de Archivos Abiertos" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memoria" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Utilización Máxima de Memoria" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Acerca de" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Autenticación" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Ancho de Banda" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Conectividad" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Uso del disco" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Configuración" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Recursos" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Interfaz de Usuario" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Elementos de menú contextual" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Columnas de detalles de torrent" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Idioma" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Región" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Preferencia de selector de etiquetas" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Selección múltiple" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Selección única" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Mostrar" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Activado" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "En el modo expandido, las etiquetas se ven mejor al final de la lista." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Exhibición de Torrents" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Tamaño de Torrent" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Modo Condensado" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Modo Expandido" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Fuentes" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Cerrar sesión" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Ajustes" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Limites de Velocidad" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Buscar torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DESARROLLO" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "ACTUALIZAR" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Descargado" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Subido" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Sín Límite" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Gratis" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Uso del disco" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Usado" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "de" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Agregar Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Valor opcional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destinación" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destinación" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Agregar Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Iniciar Descarga" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Crear" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "o haga clic para navegar" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Arrastrar archivos hacia aquí," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Con Archivo" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "URL de Torrent o Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Con URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Etiquetas" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Archivo base o directorio opcional del torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Nombre Base" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Comentario opcional en archivo torrent" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comentario" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Entrada opcional de origen en infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Fuente" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privado" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Fuente" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Etiquetas en Flood. No añadido al torrent creado." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "URL del rastreador" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Rastreadores" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Usar como Base Path" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completado" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pausar" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Comenzar" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Parar" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Detalles" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Archivos" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Archivo" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Archivos" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Cargando detalles del archivo..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comentario" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " conectados de " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Agregado en" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Creado en" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Descargado" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Espacio de Disco Libre" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "General" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrente" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Rastreador" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transferencia" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Carpeta" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Ninguno" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Pares" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Programador" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignorado" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Seguido" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Semillas" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Tamaño" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Etiquetas" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Mensaje de Tracker" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Tipo" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privado" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Publico" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Pares" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "No existen Peers para este torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " archivo seleccionado" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " archivos seleccionados" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Ajustar Prioridad" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Rastreadores" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "No existe información de tracker para este torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Tipo" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "No se puede conectar al cliente." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Borrar Filtros" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Verificar Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Detalles de Torrent" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Descargar" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Configurar Carpeta de Descarga" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pausar" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Prioridad" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Eliminar" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Configurar Etiquetas" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Definir rastreadores" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Comenzar" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Parar" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Arrastra los archivos aquí para añadirlos." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "No hay torrents" + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Configurar" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Guardando..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Comprobar hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Mover archivos" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Configurar Carpeta de Descarga" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comentario" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Creado en" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Agregado en" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Ubicación" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Velocidad de Descarga" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Cant. Descargada" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Espcio de Disco Libre" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignorar Programador" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privado" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Nombre" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Pares" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Porcentaje Descargado" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Semillas" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Tamaño de Archivo" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Etiquetas" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Mensaje de Tracker" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Rastreadores" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Velocidad de Subida" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Cant. Subida" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Eliminar Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "¿Seguro que quiere eliminar " + }, + { + "offset": 0, + "options": { + "=0": { + "value": [ + { + "type": 0, + "value": "cero torrents" + } + ] + }, + "=1": { + "value": [ + { + "type": 0, + "value": "un torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Eliminar datos" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "No ha seleccionado un torrent." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Configurar" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Introducir etiquetas" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Configurar Etiquetas" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Definir rastreadores" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Introduzca un rastreador" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Definir rastreadores" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Cargando rastreadores..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Ordenar Por" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GR" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "Mb" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "h" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/fi.json b/client/src/javascript/i18n/compiled/fi.json new file mode 100644 index 000000000..ad6541fc4 --- /dev/null +++ b/client/src/javascript/i18n/compiled/fi.json @@ -0,0 +1,2616 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Lisää Torrentti" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Poista Torrentti" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Käynnistä Torrentti" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Lopeta Torrentti" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Asetukset tallennettu onnistuneesti." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Onnistuneesti lisättiin " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " ei voitu lisätä " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Onnistuneesti siirretty " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrentit" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " ei voitu siirtää " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Onnistuneesti poistettiin " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrentit" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " poisto epäonnistui, " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Lisää Käyttäjä" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Ylläpitäjä" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Luo Tili" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Luo tili" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Tervetuloa tulvaan!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Nykyinen Käyttäjä" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Salasana ei voi olla tyhjä." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Käyttäjätunnus ei voi olla tyhjä." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Kirjaudu Sisään" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Kirjaudu" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Kirjaudu sisään tilillesi." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Käyttäjä ei ole ylläpitäjä" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Salasana" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Käyttäjän Tilit" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Käyttäjätunnus" + } + ], + "button.add": [ + { + "type": 0, + "value": "Lisää" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Peruuta" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Lataa" + } + ], + "button.new": [ + { + "type": 0, + "value": "Uusi" + } + ], + "button.no": [ + { + "type": 0, + "value": "Ei" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Tallenna Asetukset" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Tallenna" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Lisätään..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Kyllä" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Asiakasta ei voi yhdistää" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Yhteyttä ei voitu todentaa." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Asiakas" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Yhteyden asetukset eivät voi olla tyhjä." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Salasana" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Salasana" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL-osoite" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Käyttäjätunnus" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Käyttäjätunnus" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Isäntä" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Palvelimen nimi tai IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Portti" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Portti" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Polku" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Polku pistorasiaan" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Yhteyden Tyyppi" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Ei voi yhdistää asiakasta. Ole hyvä ja päivitä yhteysasetuksia." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Yhteyden Ongelma" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Ilmoitukset" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent- Luettelo" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent-Taksonomia" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Tietojen Siirron Historia" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Tietojen Siirron Hintatiedot" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Lisää Lataussääntö" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Lisää Syöte" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Sovellettava Syöte" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Käytä Tunnisteita" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Selaa syötteitä" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Ohita" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Ohita Kuvio" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Olemassa Olevat Syötteet" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Voimassa Olevat Säännöt" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Aikaväli" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Tunniste" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Osuma" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " ottelua" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " osumaa" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Täsmää Kuvio" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Syötteitä ei ole saatavilla." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Syötteitä ei ole määritelty." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Ei kohteita, jotka vastaavat hakutermiä." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Sääntöjä ei ole määritelty." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Hae termi" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Hae termi" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Valitse Syöte" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Aikaväli" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Aloita latauksessa" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Lataa Säännöt" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Syötteet" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent- Syötteet" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tunnisteet" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Testaa Vastaavuus Kuvio" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Päivää" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Tuntia" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minuuttia" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent- Kohde" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL-osoite" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Aikavälin on oltava positiivinen kokonaisluku." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Virheellinen säännöllinen lauseke." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Sinun täytyy valita syöte." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Sinun on määritettävä määränpää." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Sinun on määritettävä nimi." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Sinun on määritettävä kelvollinen syötteen URL-osoite." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Tyhjä hakemisto." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Tulvalla ei ole oikeuksia lukea tätä hakemistoa." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Tätä polkua ei ole olemassa. Se luodaan." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Tapahtui tuntematon virhe. Yritä uudelleen." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Haetaan kansioraketta..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Vanhemman Hakemisto" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Kaikki" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Aktiivinen" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Tarkistetaan" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Ladataan" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Virhe" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Passiivinen" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Lähetetään" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Pysäytetty" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Suodata tilan mukaan" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Suodata tunnisteen mukaan" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Suodata seurantapalvelimen mukaan" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Merkitsemätön" + } + ], + "general.ago": [ + { + "type": 0, + "value": "sitten" + } + ], + "general.at": [ + { + "type": 0, + "value": "klo" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Kopioitu" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Kopioi" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Tapahtui tuntematon virhe" + } + ], + "general.of": [ + { + "type": 0, + "value": "jostakin" + } + ], + "general.to": [ + { + "type": 0, + "value": "päättyen" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automaattinen" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Palvelimella tapahtui virhe. Tarkista, että mediainfo on asennettu ja saatavilla PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Haetaan..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfon Ulostulo" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Tyhjennä Kaikki" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Syötteen Kohdetta Jonossa" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Näytetään" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Virhe Raportoitu" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Lataaminen Valmis" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Älä Lataa" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Korkea" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Matala" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normaali" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Tietoja Tulvasta" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Lataa Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Lataa Paikkoja Torrenttia Kohti" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Paikan Saatavuus" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Lataa Slots Globaali" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Lataa Paikkoja Torrenttia Kohti" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Pudotusvalikon Esiasetukset: Lataus" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Pudotusvalikon Esiasetukset: Lataus" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Maailmanlaajuinen Latausnopeus Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Yleinen Lähetysnopeus Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Siirto^hinta Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Ota Dht Käyttöön" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "Dht Portti" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Hajautettu Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Tulevat Yhteydet" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Raportoitu IP/isäntänimi" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maksimi Http Yhteydet" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Ota Käyttöön Vertaisvaihto" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Käyttäjät Haluttu" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Käyttäjät" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Käyttäjien Enimmäismäärä" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Käyttäjien Minimi" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Käyttäjien Maksimi Lähetys" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Käyttäjien Vähimmäismäärä" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Avaa Portti" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Satunnaista Portti" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Portti Alue" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Levynkäytön Pisteet" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Näytä" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Vahvista täydennyksen vihje" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Oletus Lataus Hakemisto" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Levy" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Enimmäismäärä Avoimia Tiedostoja" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Muisti" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Maksimi Muistin Käyttö" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Tietoja" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Todennus" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Kaistanleveys" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Yhteys" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Levyn Käyttö" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Asetukset" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resurssit" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Käyttöliittymä" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Kontekstivalikon Nimikkeet" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent- Yksityiskohtaiset Sarakkeet" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Kieli" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Lokaatio" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Näytä" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Käytössä" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "Laajennetussa näkymässä tunnisteet toimivat parhaiten listan lopussa." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent- Listan Näyttö" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrenttien Koko" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Tiivistetty Näkymä" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Laajennettu Näkymä" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Syötteet" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Kirjaudu Ulos" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Asetukset" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Nopeusrajat" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Etsi torrent-tiedostoja" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "LATAA" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "LATAA" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Ladattu" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Ladattu" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Rajoittamaton" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Ilmainen" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Levyn Käyttö" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Yhteensä" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Käytetty" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "jostakin" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Lisää Torrentti" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Valinnainen evästeen nimi = evästeen arvo" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Evästeet" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Kohde" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Kohde" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Lisää Torrentit" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Käynnistä Torrentti" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Luo" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "tai klikkaa selataksesi" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Pudota joitakin tiedostoja tähän." + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Tiedoston Mukaan" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent- URL tai Magnet- linkki" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "URL:n Mukaan" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tunnisteet" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrentit" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Valinnainen perustiedosto tai torrent-tiedoston nimi" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Perusnimi" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Valinnainen kommentti torrent-tiedostossa" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Kommentti" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Valinnainen lähdemerkintä infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Tietojen Lähde" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Yksityinen" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Lähde" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tunnisteet Tulossa. Ei lisätty luotuun torrenttiin." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Seurantapalvelimen URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Seurantapalvelimet" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Käytä peruspolkuna" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Valmis" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Tauko" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Aloita" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Pysäytä" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Yksityiskohdat" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Tiedostot" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Lataa tiedosto" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Lataa Tiedostoja" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Ladataan tiedoston tietoa..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Kommentti" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " yhdistetty " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Lisätty" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Luonti Päivämäärä" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Ladattu" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Vapaa Levytila" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Yleiset" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrentti" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Seurantapalvelin" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Siirto" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Sijainti" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Ei Mitään" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Käyttäjät" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Ajastus" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ohitettu" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Ylimääräinen" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Siemenet" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Koko" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tunnisteet" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Seurantapalvelimen Viesti" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Tyyppi" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Yksityinen" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Julkinen" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Käyttäjät" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Tälle torrentille ei ole vertaistietoja." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " valittu tiedosto" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " valitut tiedostot" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Aseta Prioriteetti" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Seurantapalvelimet" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Tälle torrentille ei ole seurantatietoja." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Tyyppi" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Ei voi muodostaa yhteyttä asiakkaaseen." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Tyhjennä Suodattimet" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Tarkista Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent-tiedoston Tiedot" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Lataa" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Aseta Torrent- Sijainti" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Tauko" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Prioriteetti" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Poista" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Aseta Tagit" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Aseta Seurantapalvelimet" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Aloita" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Pysäytä" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Pudota tiedostot tähän lisätäksesi ne." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Ei näytettäviä torrent-tiedostoja." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Aseta Sijainti" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Asetetaan..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Tarkista tiivistys" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Siirrä tiedot" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Aseta Torrent- Sijainti" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Kommentti" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Luonti Päivämäärä" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Lisätty" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Latauksen Nopeus" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Ladattu" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Vapaa Levytila" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ohita Ajastin" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Yksityinen" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Nimi" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Käyttäjät" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Prosentti Valmis" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Suhde" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Siemenet" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Tiedoston Koko" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tunnisteet" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Seurantapalvelimen Viesti" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Seurantapalvelimet" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Lähetyksen Nopeus" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Ladattu" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Poista Torrentit" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Oletko varma, että haluat poistaa " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Poista tiedot" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Et ole valinnut yhtään torrenttia." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Aseta Tagit" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Syötä tunnisteet" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Aseta Tagit" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Aseta Seurantapalvelimet" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Anna seurantapalvelin" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Aseta Seurantapalvelimet" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Ladataan seurantaohjelmia..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Järjestä Mukaan" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "Gt" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kt" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "Mt" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "Tt" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "pv" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "h" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "vk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/fr.json b/client/src/javascript/i18n/compiled/fr.json new file mode 100644 index 000000000..5fd18bd87 --- /dev/null +++ b/client/src/javascript/i18n/compiled/fr.json @@ -0,0 +1,2652 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Ajouter un torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Supprimer le torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Démarrer le torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Arrêter le torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Paramètres modifiés." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "L'ajout de " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " a réussi." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "L'ajout de " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " a échoué." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Le déplacement de " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " a réussi." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Le déplacement de " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " a échoué." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "La suppression de " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " a réussi." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "La suppression de " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " a échoué." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Ajouter un utilisateur" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Administrateur" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Créer un compte" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Créer un compte" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Bienvenue dans Flood !" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Utilisateur actuel" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Le mot de passe ne peut pas être vide." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Le nom d'utilisateur ne peut pas être vide." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Effacer" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Connexion" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Identifiant" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Se connecter." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "L'utilisateur n'est pas administrateur" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Mot de passe" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Comptes" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Nom d'utilisateur" + } + ], + "button.add": [ + { + "type": 0, + "value": "Ajouter" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Annuler" + } + ], + "button.close": [ + { + "type": 0, + "value": "Fermer" + } + ], + "button.download": [ + { + "type": 0, + "value": "Télécharger" + } + ], + "button.new": [ + { + "type": 0, + "value": "Nouveau" + } + ], + "button.no": [ + { + "type": 0, + "value": "Non" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Réessayer" + } + ], + "button.save": [ + { + "type": 0, + "value": "Enregistrer les paramètres" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Enregistrer" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Ajout..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Oui" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Mettre à jour les paramètres de connexion client" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Réessayer avec les paramètres de connexion actuels du client" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Impossible de se connecter au client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Veuillez contacter votre administrateur Flood si cela persiste." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "La connexion n'a pas pu être établie." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Les paramètres de connexion ne peuvent pas être vides." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Mot de passe" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Mot de passe" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL de l'API Web qBittorrent" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Nom d'utilisateur" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Nom d'utilisateur" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Hôte" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Nom d'hôte ou IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Chemin d'accès" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Chemin d'accès au socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Type de connexion" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposer rTorrent via TCP peut permettre une élévation des privilèges." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Mot de passe" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Mot de passe" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL de l'interface RPC de Transmission" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Nom d'utilisateur" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Nom d'utilisateur" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Impossible de se connecter au client. Veuillez mettre à jour les paramètres de connexion." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Problème de connectivité" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Liste des torrents" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Taxonomie du torrent" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Historique des transferts de données" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Détails du taux de transfert des données" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Ajouter une règle de téléchargement" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Ajouter un flux" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable au flux" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Appliquer les tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Parcourir les flux" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Valider la règle en l'essayant. Non enregistré ou envoyé." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclure" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Motif pour exclusion" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Flux existants" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Règles existantes" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Intervalle" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Étiquette" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Correspondance" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=0": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " correspond" + } + ] + }, + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " correspond" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " correspondent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Motif pour inclusion" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Aucun flux disponible." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Aucun flux défini." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Aucun élément ne correspond à la recherche." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Aucune règle définie." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "Expression régulière" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Recherche" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Recherche" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Sélectionner un flux" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Intervalle" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Démarrer immédiatement" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Règles de téléchargement" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Flux" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Flux de torrents" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Tester le motif" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Jours" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Heures" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutes" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Emplacement du torrent" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "L'intervalle doit être un entier positif." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Expression régulière invalide." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Vous devez choisir un flux." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Vous devez définir un emplacement." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Vous devez définir un label." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Vous devez définir une URL de flux valide." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Répertoire vide." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood n'a pas la permission de lire ce répertoire." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Cet emplacement n'existe pas. Il sera créé." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Une erreur inconnue s'est produite. Veuillez réessayer." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Récupération de la structure du répertoire..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Dossier parent" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Tout" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Actif" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "En cours de vérification" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Terminé" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "En téléchargement" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Erreur" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactif" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "En source" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Arrêté" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtrer par Status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtrer par étiquette" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtrer par tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Sans étiquette" + } + ], + "general.ago": [ + { + "type": 0, + "value": "il y a" + } + ], + "general.at": [ + { + "type": 0, + "value": "à" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copié" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copier" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Une erreur inconnue s'est produite" + } + ], + "general.of": [ + { + "type": 0, + "value": "de" + } + ], + "general.to": [ + { + "type": 0, + "value": "à" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatique" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Commencer à traduire" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Une erreur est survenue lors de l'exécution de mediainfo sur le serveur. Vérifiez que mediainfo est installé et disponible dans le PATH de Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Récupération en cours..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Résultat mediainfo" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Tout effacer" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Élément de flux en file d'attente" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "Aucune notification à afficher." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Affichage de" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Erreur signalée" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Téléchargement terminé" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Ne pas télécharger" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Élevée" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Basse" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normale" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "À propos de Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Slots de téléchargement globaux" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Slots de téléchargement par torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Configuration des slots" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Slots d'envoi global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Slots d'essaimage par torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Pré-réglages des vitesses : téléchargement" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Pré-réglages des vitesses : Envoi" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Limite globale de téléchargement" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Limite globale d'envoi" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Limites de vitesse de transfert" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Activer le DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "Port DHT" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Découverte décentralisée de pairs" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Connexions entrantes" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Adresse IP / nom d'hôte affichée" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Limite de connexions HTTP" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Activer l'échange de pairs" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Pairs désirés" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Pairs" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Limite de pairs en téléchargement" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Seuil minimum de pairs" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Seuil maximum de pairs en source" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Seuil minimum de pairs en essaimage" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Ouvrir le port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Port aléatoire" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Plage de ports" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Points de montage à observer" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Afficher" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Vérifier le hash après le téléchargement" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Répertoire de téléchargement par défaut" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disque" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Limite de fichiers ouverts" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Mémoire" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Limite d'utilisation de mémoire" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "À propos" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentification" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bande passante" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectivité" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Utilisation du disque" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Paramètres" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Ressources" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Interface utilisateur" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Éléments du menu contextuel" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Colonnes de détails du torrent" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Langue" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Paramètres régionaux" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Sélecteur de Préférence" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Sélection Multiple" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Sélection Unique" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Afficher" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Activé" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "Dans la vue étendue, il vaut mieux placer les tags dernier." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Affichage de la liste des torrents" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Taille du torrent" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Vue condensée" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Vue étendue" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Flux" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Déconnexion" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Paramètres" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Limites de Vitesse" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Thème Sombre" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Thème Clair" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Rechercher les torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "TÉLÉCHARGEMENT" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "ENVOI" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Téléchargé" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Envoyé" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Illimité" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Disponible" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Utilisation du disque" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Utilisé" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "sur" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Ajouter un torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "cookie-name=cookie-value optionnel" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Emplacement" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Emplacement" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Ajouter des torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Démarrer" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Créer" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "ou cliquez pour sélectionner" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Glissez-déposez des fichiers ici," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Par fichier" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "URL du torrent ou lien magnet" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "S'enregistrer pour gérer les liens magnet" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Par URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Fichier de base optionnel ou nom de répertoire du torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Nom de base" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Commentaire optionnel dans le fichier torrent" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Commenter" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Saisie optionnelle de la source dans le tableau de bord" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Source d'info" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privé" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags dans le Déluge. Non ajouté au torrent créé." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "URL du tracker" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Utiliser comme chemin de base" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Terminé" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Démarrer" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Arrêter" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Détails" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Fichiers" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Téléchargez le fichier" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Téléchargez les fichiers" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Chargement des détails du fichier..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Commentaire" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connectés sur " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Ajouté" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Créé" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Téléchargé" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Espace disque disponible" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Général" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfert" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Emplacement" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Aucun" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Pairs" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Planificateur" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignoré" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Conforme" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Sources" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Taille" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Message du tracker" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type de texte" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privé" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Pairs" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Il n'y a aucun pair pour ce torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=0": { + "value": [ + { + "type": 0, + "value": "aucun fichier sélectionné" + } + ] + }, + "=1": { + "value": [ + { + "type": 0, + "value": "un fichier sélectionné" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " fichiers sélectionnés" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Définir la priorité" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Il n'y a aucun tracker actif pour ce torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Générer un lien Magnet" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Chargement des trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Lien Magnet" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Lien Magnet avec Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "C'est un torrent privé." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Impossible de se connecter au client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Effacer les filtres" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Vérifier le hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Détails du torrent" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Télécharger" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Générer un lien Magnet" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Définir l'emplacement du torrent" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Mettre en pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priorité" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Supprimer" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Définir les tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Définir les mouchards" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Démarrer" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Arrêter" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Déposez les fichiers ici pour les ajouter." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Aucun torrent à afficher." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Définir l'emplacement" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "En cours..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Vérifier le hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Déplacer les données" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Définir l'emplacement du torrent" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Commentaire" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Date de création" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Ajouté" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Emplacement" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Vitesse de téléchargement" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Téléchargé" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "Date de fin estimée" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Espace libre" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignorer la planification" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privé" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Nom" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Pairs" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "% terminé" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Sources" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Taille" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Message du tracker" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Vitesse d'Envoi" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Données envoyées" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Supprimer les torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Confirmez-vous la suppression " + }, + { + "offset": 0, + "options": { + "=0": { + "value": [ + { + "type": 0, + "value": "d'aucun torrent" + } + ] + }, + "=1": { + "value": [ + { + "type": 0, + "value": "d'un torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "de " + }, + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " ?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Supprimer les données" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Vous n'avez sélectionné aucun torrent." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Définir les tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Saisissez les tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Définir les tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Définir les mouchards" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Entrez un tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Définir les mouchards" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Chargement des trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Trier par" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "o" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "Go" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "ko" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "Mo" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "To" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "j" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "h" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "min" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "sem" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "an" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/he.json b/client/src/javascript/i18n/compiled/he.json new file mode 100644 index 000000000..876af72fd --- /dev/null +++ b/client/src/javascript/i18n/compiled/he.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Remove Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Successfully saved settings." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Successfully added " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Failed to add " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Successfully moved " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Failed to move " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Successfully removed " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Failed to remove " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Add User" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Create Account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Create an account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Welcome to Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Current User" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Username cannot be empty." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log In" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Login" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in to your account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "User is not Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Password" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "User Accounts" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Username" + } + ], + "button.add": [ + { + "type": 0, + "value": "Add" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancel" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "New" + } + ], + "button.no": [ + { + "type": 0, + "value": "No" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Save Settings" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Save" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Adding..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Yes" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Connection could not be verified." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Connectivity Issue" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent List" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Transfer History" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Add Download Rule" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Add Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Apply Tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Browse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclude" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exclude Pattern" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Existing Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Existing Rules" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Label" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Match" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matches" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match Pattern" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "No feeds available." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "No feeds defined." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No items matching search term." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "No rules defined." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Select Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start on load" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Download Rules" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Days" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Hours" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutes" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "The interval must be a positive integer." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Invalid regular expression." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "You must select a feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "You must specify a destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "You must specify a label." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "You must specify a valid feed URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Empty directory." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood does not have permission to read this directory." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "This path does not exist. It will be created." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Fetching directory structure..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Parent Directory" + } + ], + "filter.all": [ + { + "type": 0, + "value": "All" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Active" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Checking" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloading" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Error" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactive" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stopped" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filter by Status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filter by Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filter by Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Untagged" + } + ], + "general.ago": [ + { + "type": 0, + "value": "ago" + } + ], + "general.at": [ + { + "type": 0, + "value": "at" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copied" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copy" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "of" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatic" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Fetching..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Output" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Clear All" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Showing" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Error Reported" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Finished Downloading" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Don't Download" + } + ], + "priority.high": [ + { + "type": 0, + "value": "High" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Low" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "About Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download Slots Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slot Availability" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload Slots Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown Presets: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown Presets: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global Download Rate Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global Upload Rate Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Transfer Rate Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Enable DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralized Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Incoming Connections" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Reported IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximum HTTP Connections" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Enable Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Peers Desired" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximum Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimum Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximum Peers Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum Peers Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Open Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Randomize Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Range" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verify Hash on Completion" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Default Download Directory" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximum Open Files" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memory" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Max Memory Usage" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentication" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandwidth" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectivity" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Settings" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resources" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "User Interface" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Language" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Locale" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Enabled" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In the expanded view, tags work best at the end of the list." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent List Display" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Size" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Condensed View" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Expanded View" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log Out" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Settings" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Speed Limits" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Search torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Unlimited" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Free" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Used" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "of" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Add Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "or click to browse" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Drop some files here," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "By File" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL or Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "By URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Use as Base Path" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Details" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Files" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download File" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download Files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Loading file detail..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connected of " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "General" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfer" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "None" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Scheduler" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignored" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Size" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "There is no peer data for this torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected file" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Set Priority" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "There is no tracker data for this torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Clear Filters" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Check Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priority" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Remove" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "No torrents to display." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Set Location" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Setting..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Check hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Move data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Download Speed" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignore Scheduler" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Name" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percent Complete" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "File Size" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Upload Speed" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Remove Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Are you sure you want to remove " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Delete data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "You haven't selected any torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Enter tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sort By" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "hr" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/hu.json b/client/src/javascript/i18n/compiled/hu.json new file mode 100644 index 000000000..57d45bdaa --- /dev/null +++ b/client/src/javascript/i18n/compiled/hu.json @@ -0,0 +1,2632 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Torrent hozzáadása" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Torrent eltávolítása" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Torrent indítása" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Torrent megállítása" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Beállítások sikeresen elmentve." + } + ], + "alert.torrent.add": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrentek" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " sikeresen hozzáadva." + } + ], + "alert.torrent.add.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrentet" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenteket" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " nem sikerült hozzáadni." + } + ], + "alert.torrent.move": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "one": { + "value": [] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " sikeresen áthelyezve." + } + ], + "alert.torrent.move.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrentet" + } + ] + }, + "one": { + "value": [] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrentet" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " nem sikerült áthelyezni." + } + ], + "alert.torrent.remove": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "one": { + "value": [] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " sikeresen eltávolítva." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "one": { + "value": [] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " eltávolítása sikertelen." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Új felhasználó" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Adminisztrátor" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Fiók létrehozása" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Fiók létrehozása" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Üdvözöli a Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Jelenlegi felhasználó" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "A felhasználónév nem lehet üres." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Bejelentkezés" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Bejelentkezés" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Jelentkezz be a fiókodba." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "A felhasználó nem Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Jelszó" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Felhasználói fiókok" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Felhasználónév" + } + ], + "button.add": [ + { + "type": 0, + "value": "Hozzáadás" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Mégse" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Letöltés" + } + ], + "button.new": [ + { + "type": 0, + "value": "Új" + } + ], + "button.no": [ + { + "type": 0, + "value": "Nem" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Beállítások mentése" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Mentés" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Hozzáadás..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Igen" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "A kapcsolatot nem sikerült ellenőrizni." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Kapcsolódási hiba" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Értesítések" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent lista" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Adatátviteli előzmények" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Adatátviteli sebesség részletei" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Letöltési szabály hozzáadása" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Hírcsatorna hozzáadása" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Címkék hozzáadása" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Hírcsatornák böngészése" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Kivéve" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Kizárási minta" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Rendelkezésre álló hírcsatornák" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Rendelkezésre álló szabályok" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Intervallum" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Megnevezés" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Egyezés" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " egyezés" + } + ] + }, + "one": { + "value": [] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " egyezés" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Egyezési minta" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Nincs elérhető hírcsatorna." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Nincs megadva hírcsatorna." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "A keresési feltételnek egyetlen elem sem felel meg." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Nincs megadva szabály." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Keresési kifejezés" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Keresési kifejezés" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Csatorna kiválasztása" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Intervallum" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Indítás azonnal" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Letöltési szabályok" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Hírcsatornák" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent hírcsatornák" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Címkék" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Egyezési minta tesztelése" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Nap" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Óra" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Perc" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Letöltési könyvár helye" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Az intervallum pozitív egész szám kell legyen." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Érvénytelen szabályos kifejezés." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Választanod kell egy csatornát." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Meg kell adnod egy célkönyvtárat." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Meg kell adnod egy nevet." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Érvényes hírcsatorna URL-t adj meg." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Üres könyvtár." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "A Flood-nak nincs jogosultsága olvasni ezt a könyvtárat." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Az elérési út nem létezik, így létre lesz hozva." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Könyvtárszerkezet lekérdezése..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Szülőkönyvtár" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Mind" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Aktív" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Ellenőrzés alatt" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Befejezett" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Letöltés alatt" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Hiba" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inaktív" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Megállítva" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Szűrés állapot szerint" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Szűrés Címke alapján" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Szűrés Tracker alapján" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Címke nélküli" + } + ], + "general.ago": [ + { + "type": 0, + "value": "ezelőtt" + } + ], + "general.at": [ + { + "type": 0, + "value": "-kor" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Másolva" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Másolás" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "of" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatikus" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Hiba történt a mediainfo futtatásakor a szerveren. Ellenőrizd, hogy a mediainfo telepítve van és elérhető a Flood számára." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Lekérés..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "A Mediainfo válasza" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Összes törlése" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Mutat" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Hibajelzés" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Letöltés befejeződött" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Ne töltsd" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Magas" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Alacsony" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normál" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "A Flood-ról" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Globális letöltési szál" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Letöltési szál per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Átviteli szálak meghatározása" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Globális feltöltési szál" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Feltöltési szál per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Legördülő lista értékei: Letöltés" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Legördülő lista értékei: Feltöltés" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Globális letöltési sebességkorlát" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Globális feltöltési sebességkorlát" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Adatátviteli sebességkorlátok" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "DHT engedélyezése" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralizált Peer-ek felderítése" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Bejövő kapcsolatok" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Közzétett IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Max. HTTP kapcsolatok száma" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Peer csere engedélyezése" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Kívánt Peer-ek száma" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peer-ek" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Max. Peer-ek száma" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Min. Peer-ek száma" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Seedeléskor max. Peer-ek száma" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Seedeléskor min. Peer-ek száma" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Nyitott port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Véletlenszerű port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port tartomány" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Megjelenítés" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Hash ellenőrzés a letöltés végén" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Alapértelmezett letöltési könyvtár" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Lemez" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Max. megnyitott fájlok száma" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memória" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Max. memóriahasználat" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Névjegy" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Azonosítás" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Sávszélesség" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Kapcsolat" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Lemezhasználat" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Beállítások" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Erőforrások" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Felhasználói felület" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Nyelv" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Területi beállítások" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Engedélyezve" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "Kibővített nézetben a címkék megjelenítése a lista végén ajánlott." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent lista megjelenítése" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent mérete" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Szűkített nézet" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Bővített nézet" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Hírcsatornák" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Kijelentkezés" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Beállítások" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Sebességkorlát" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Torrentek keresése" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "LETÖLTÉS" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "FELTÖLTÉS" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Letöltve" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Feltöltve" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Korlátlan" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Szabad" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Lemezhasználat" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Összesen" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Felhasznált" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "of" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Torrent hozzáadása" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Célkönyvtár" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Célkönyvtár" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Torrentek hozzáadása" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Torrent indítása" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "vagy kattints a böngészéshez" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Húzd ide a fájlokat," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Fájl alapján" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL vagy Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "URL alapján" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Címkék" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrentek" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Használd kiindulási pontként" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Szüneteltetés" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Indítás" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Leállítás" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Részletek" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Fájlok" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Fájl letöltése" + } + ] + }, + "one": { + "value": [] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Fájlok letöltése" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Fájl adatainak betöltése..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Megjegyzés" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " csatlakozva a " + }, + { + "type": 1, + "value": "total" + }, + { + "type": 0, + "value": "-ból/ből" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Hozzáadva" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Létrehozás dátuma" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Letöltve" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Szabad lemezterület" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Általános" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Átvitel" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Tartózkodási hely" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Nincs" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peer-ek" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Ütemező" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Mellőzve" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seed-ek" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Méret" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Címkék" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker üzenete" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Típus" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privát" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Nyilvános" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peer-ek" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Nincs elérhető peer információ ehhez a torrenthez." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " kiválasztott fájl" + } + ] + }, + "one": { + "value": [] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " kiválasztott fájl" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Prioritás beállítása" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Tracker-ek" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Nincs elérhető tracker információ ehhez a torrenthez." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Típus" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Szűrök törlése" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Hash ellenőrzése" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent részletei" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Letöltés helyigépre" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Áthelyezés" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Szüneteltetve" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Prioritás" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Törlés" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Címke hozzárendelése" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Indítás" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Leállítás" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Nincs megjeleníthető torrent." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Set Location" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Beállítás..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Hash ellenőrzése" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Adat áthelyezése" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Torrent áthelyezése másik könyvtárba" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Megjegyzés" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Létrehozás dátuma" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Hozzáadva" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Letöltési sebesség" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Letöltve" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "Becsült idő" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Szabad lemezterület" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ütemező figyelmen kívül hagyása" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privát" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Név" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peer-ek" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Százalék készen" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Arány" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seed-ek" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Fájlméret" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Címkék" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker üzenete" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Tracker-ek" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Feltöltési sebesség" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Feltöltve" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Torrent-ek eltávolítása" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Biztosan el akarod távolítani ezt a/az " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent-tet" + } + ] + }, + "one": { + "value": [] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent-tet" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Adat törlése" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Nem jelöltél ki egyetlen torrentet sem." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Címke hozzárendelése" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Címke felvétele" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Címke hozzárendelése" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Rendezés" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "nap" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "óra" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "p" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "mp" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "hét" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "év" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/it.json b/client/src/javascript/i18n/compiled/it.json new file mode 100644 index 000000000..f3aa74528 --- /dev/null +++ b/client/src/javascript/i18n/compiled/it.json @@ -0,0 +1,2604 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Aggiungi Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Rimuovi Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Avvia Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Ferma Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Impostazioni salvate con successo." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Hai aggiunto con successo " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Impossibile aggiungere " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Spostato con successo " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Impossibile spostare " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Hai rimosso con successo " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Impossibile rimuovere " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Aggiungi Utente" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Amministratore" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Crea Account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Crea un account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Benvenuto a Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Utente Attuale" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "La password non può essere vuota." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Il nome utente non può essere vuoto." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log In" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Accedi" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Accedi al tuo account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "L'utente non è amministratore" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Password" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Account Utente" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Username" + } + ], + "button.add": [ + { + "type": 0, + "value": "Aggiungi" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Annulla" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Scarica" + } + ], + "button.new": [ + { + "type": 0, + "value": "Nuovo" + } + ], + "button.no": [ + { + "type": 0, + "value": "No" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Salva Impostazioni" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Salva" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Aggiungi..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Sì" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Impossibile connettersi al client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "La connessione non può essere verificata." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Le impostazioni di connessione non possono essere vuote." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL a qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname o IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Porta" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Porta" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Percorso" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Percorso del socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Tipo Di Connessione" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Impossibile connettersi al client. Si prega di aggiornare le impostazioni di connessione." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Problema Di Connettività" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifiche" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Lista Torrent" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Tassonomia Torrent" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Cronologia Trasferimento Dati" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Dettagli" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Aggiungi Regola Download" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Aggiungi Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Alimenti Applicabili" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Applica Etichette" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Sfoglia i feed" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Escludi" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Escludi Motivo" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Feed Esistenti" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Regole Esistenti" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Intervallo" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Etichetta" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Partita" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " partita" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " partite" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Modello Di Partita" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Nessun feed disponibile." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Nessun feed definito." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Nessun elemento corrispondente al termine di ricerca." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Nessuna regola definita." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Termine di ricerca" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Termine di ricerca" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Seleziona Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Intervallo" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Avvia al caricamento" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Scarica Regole" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feed" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Feed Torrent" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Etichette" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Motivo Partita Di Prova" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Giorni" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Ore" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minuti" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Destinazione Torrent" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "L'intervallo deve essere un numero intero positivo." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Espressione regolare non valida." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Devi selezionare un feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "È necessario specificare una destinazione." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "È necessario specificare un'etichetta." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "È necessario specificare un URL feed valido." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Directory vuota." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood non ha il permesso di leggere questa directory." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Questo percorso non esiste. Sarà creato." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Si è verificato un errore sconosciuto. Riprova." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Recupero struttura directory..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Cartella Superiore" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Tutti" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Attivo" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Controllo" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Scaricamento" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Errore" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inattivo" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Semina" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Fermato" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtra per stato" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtra per Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtra per Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Untagged" + } + ], + "general.ago": [ + { + "type": 0, + "value": "fa" + } + ], + "general.at": [ + { + "type": 0, + "value": "a" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copiato" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copia" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Si è verificato un errore sconosciuto" + } + ], + "general.of": [ + { + "type": 0, + "value": "di" + } + ], + "general.to": [ + { + "type": 0, + "value": "a" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatico" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Si è verificato un errore durante l'esecuzione di mediainfo sul server. Verificare che mediainfo sia installato e disponibile nel PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Recupero..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Uscita Mediainfo" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Cancella Tutto" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Accodato" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Mostrando" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Errore Segnalato" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Scaricamento Finito" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Non Scaricare" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Alto" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Basso" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normale" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Informazioni Sull'Inondazione" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Scarica Slot Globali" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Scarica Slot Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Disponibilità Slot" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Carica Slot Globali" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Carica Slot Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Preimpostazioni A Discesa: Scarica" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Preimpostazioni A Discesa: Carica" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Velocità Di Download Globale Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Acceleratore Globale Del Tasso Di Caricamento" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Velocità Di Trasferimento Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Abilita DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "Porta DHT" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Scoperta Peer Decentralizzata" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Connessioni In Entrata" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Ip/Hostname Segnalato" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Connessioni HTTP Massime" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Abilita Scambio Peer" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Pari Desiderati" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Peers Massimo" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Peers Minimo" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Massima Seeding Dei Peers" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Seeding Dei Peers Minimi" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Porta Aperta" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Porta Casuale" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Intervallo Di Porta" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Punti Di Montaggio Utilizzo Disco" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Mostra" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verifica l'hash al completamento" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Directory Di Download Predefinita" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disco" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "File Aperti Massimi" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memoria" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Utilizzo Massimo Della Memoria" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Informazioni" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Autenticazione" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Larghezza Di Banda" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connettività" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Uso Del Disco" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Impostazioni" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Risorse" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Interfaccia Utente" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Elementi Del Menu Contestuale" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Colonne Dettagli Torrent" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Lingua" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Localizzazione" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Mostra" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Abilitato" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "Nella vista espansa, i tag funzionano meglio alla fine dell'elenco." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Visualizzazione Elenco Torrent" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Dimensione Torrent" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Vista Condensata" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Vista Estesa" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feed" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Esci" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Impostazioni" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Limiti Di Velocità" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Cerca torrent" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "SCARICA" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "SPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Scaricato" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Caricato" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Illimitato" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Gratis" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Uso Del Disco" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Totale" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Usato" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "di" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Aggiungi Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookie" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destinazione" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destinazione" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Aggiungi Torrent" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Avvia Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Crea" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "o clicca per sfogliare" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Trascina qui alcuni file," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Per File" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Url Torrent o Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Per URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Etichette" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "File di base opzionale o nome di directory del torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Nome Base" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Commento opzionale nel file torrent" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Commento" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Voce sorgente opzionale in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Sorgente Informazioni" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privato" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Fonte" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Non aggiunto al torrent creato." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Url Del Tracker" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Usa come percorso base" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completato" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pausa" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Inizia" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Ferma" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Dettagli" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "File" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Scarica il file" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Scarica i file" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Caricamento dettagli del file..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Commento" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connesso da " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Aggiunto" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Data Di Creazione" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Scaricato" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Spazio Libero Su Disco" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Generale" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Trasferisci" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Posizione" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Nessuno" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Pianificatore" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignorato" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Semi" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Dimensione" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Etichette" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Messaggio Tracker" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Tipo" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privato" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Pubblico" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Non ci sono dati peer per questo torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " file selezionato" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " file selezionati" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Imposta Priorità" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Non ci sono dati tracker per questo torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Tipo" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Impossibile connettersi al client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Cancella Filtri" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Controlla Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Dettagli Torrent" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Scarica" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Imposta Posizione Del Torrent" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pausa" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priorità" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Rimuovi" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Imposta Etichette" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Imposta Tracker" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Inizia" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Ferma" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Trascina qui i file per aggiungerli." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Nessun torrent da visualizzare." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Imposta Posizione" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Impostazione..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Controlla hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Sposta dati" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Imposta Posizione Del Torrent" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Commento" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Data Di Creazione" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Aggiunto" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Velocità Di Download" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Scaricato" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Spazio Libero Su Disco" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignora Scheduler" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privato" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Nome" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percentuale Completata" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Rapporto" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Semi" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Dimensione File" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Etichette" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Messaggio Tracker" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Velocità Di Caricamento" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Caricato" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Rimuovi Torrent" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Sei sicuro di voler rimuovere " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Elimina dati" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Non hai selezionato alcun torrent." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Imposta Etichette" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Inserisci tag" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Imposta Etichette" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Imposta Tracker" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Inserisci un tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Imposta Tracker" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Caricamento trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Ordina Per" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "g" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "ora" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/ja.json b/client/src/javascript/i18n/compiled/ja.json new file mode 100644 index 000000000..350433f17 --- /dev/null +++ b/client/src/javascript/i18n/compiled/ja.json @@ -0,0 +1,2584 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "トレントを追加" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Torrent を削除" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Torrent を開始" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Torrent を停止" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "設定を正常に保存しました。" + } + ], + "alert.torrent.add": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " を追加しました " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.add.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " の追加に失敗しました " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " を移動しました " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " の移動に失敗しました " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " を削除しました " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " の削除に失敗しました " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "ユーザーを追加" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "管理者" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "アカウントを作成" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "アカウントを作成" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "洪水へようこそ!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "現在のユーザー" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "パスワードは空にできません。" + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "ユーザー名は空にできません。" + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "ログイン" + } + ], + "auth.login": [ + { + "type": 0, + "value": "ログイン" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "アカウントにログインします。" + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "ユーザーは管理者ではありません" + } + ], + "auth.password": [ + { + "type": 0, + "value": "パスワード" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "ユーザーアカウント" + } + ], + "auth.username": [ + { + "type": 0, + "value": "ユーザー名" + } + ], + "button.add": [ + { + "type": 0, + "value": "追加" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "キャンセル" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "ダウンロード" + } + ], + "button.new": [ + { + "type": 0, + "value": "新規作成" + } + ], + "button.no": [ + { + "type": 0, + "value": "いいえ" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "設定を保存" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "保存" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "追加中..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "はい" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "クライアントに接続できません" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "接続を確認できませんでした。" + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "クライアント" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "接続設定は空にできません。" + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrentformat@@0" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "パスワード" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "パスワード" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrentの Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "ユーザー名" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "ユーザー名" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "ホスト" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "ホスト名または IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "ポート" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "ポート" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "パス" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "ソケットへのパス" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "接続タイプ" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "クライアントに接続できません。接続設定を更新してください。" + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "接続の問題" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "通知" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent リスト" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "トレント分類" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "データ転送履歴" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "データ転送速度の詳細" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "ダウンロードルールを追加" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "フィードを追加" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "適用されるフィード" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "タグを適用" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "フィードを参照" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "除外" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "パターンを除外" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "既存のフィード" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "既存のルール" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "間隔" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "ラベル" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "一致" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "パターンに一致" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "利用可能なフィードはありません。" + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "フィードが定義されていません。" + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "検索語に一致するアイテムはありません。" + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "ルールが定義されていません。" + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "検索キーワード:" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "検索キーワード:" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "フィードを選択" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "間隔" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "ロード時に開始" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "ルールをダウンロード" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "フィード" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "トレントフィード" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "タグ" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "テストマッチパターン" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "日数" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "時間" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "分" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "トレントの宛先" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "intervalは正の整数でなければなりません。" + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "無効な正規表現です。" + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "フィードを選択してください。" + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "目的地を指定する必要があります。" + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "ラベルを指定する必要があります。" + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "有効なフィードURLを指定する必要があります。" + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "空のディレクトリ。" + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "このディレクトリを読み込む権限がありません。" + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "このパスは存在しません。作成されます。" + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "不明なエラーが発生しました。もう一度やり直してください。" + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "ディレクトリ構造を取得しています..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "親ディレクトリ" + } + ], + "filter.all": [ + { + "type": 0, + "value": "すべて" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "アクティブ" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "確認中" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "ダウンロード中" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "エラー" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "非アクティブ" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "シード中" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "停止しました" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "ステータスでフィルター" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "タグでフィルター" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "トラッカーでフィルター" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "タグなし" + } + ], + "general.ago": [ + { + "type": 0, + "value": "前" + } + ], + "general.at": [ + { + "type": 0, + "value": "に" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "コピーしました" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "コピー" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "不明なエラーが発生しました" + } + ], + "general.of": [ + { + "type": 0, + "value": "/" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "自動" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "サーバー上でmediainfoを実行中にエラーが発生しました。mediainfoがインストールされ、PATH to Floodで利用できることを確認してください。" + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "フェッチ中..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo 出力" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "すべてクリア" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "フィードアイテムがキューに追加されました" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "表示" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "報告エラー" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "ダウンロード完了" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "ダウンロードしない" + } + ], + "priority.high": [ + { + "type": 0, + "value": "高い" + } + ], + "priority.low": [ + { + "type": 0, + "value": "低い" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "標準" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "洪水について" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "グローバルスロットをダウンロード" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Torrent ごとにスロットをダウンロード" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "スロットの在庫状況" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "グローバルスロットをアップロード" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Torrent ごとにスロットをアップロード" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "ドロップダウンプリセット: ダウンロード" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "ドロップダウンプリセット: アップロード" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "グローバルダウンロードレートスロットル" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "グローバルアップロードレートスロットル" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "スロットル転送レート" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "DHTを有効にする" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHTポート" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "分散型ピアの発見" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "受信接続" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "報告されたIP/ホスト名" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "最大HTTP接続" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "ピア交換を有効にする" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "ピアが望ましいです" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "ピア" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "最大ピア数" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "ピアの最小数" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "ピアシードの最大数" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "ピアシードの最小数" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "ポートを開く" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "ポートをランダム化" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "ポート範囲" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "ディスク使用量マウントポイント" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "表示" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "完了時にハッシュを確認" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "デフォルトのダウンロードディレクトリ" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "ディスク" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "最大開いているファイル" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "メモリ" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "最大メモリ使用量" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "認証" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandwidth" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "接続" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "ディスクの使用量" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "設定" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "リソース" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "ユーザー インターフェイス" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "コンテキストメニューアイテム" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "トレントの詳細列" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "言語" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "ロケール" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "表示" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "有効" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "展開されたビューでは、タグはリストの最後に最適に動作します。" + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "トレントリストの表示" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "トレントサイズ" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Condensed View" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "展開表示" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "フィード" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "ログアウト" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "設定" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "速度制限" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Torrent を検索" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "ダウンロード" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "アップロード" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "ダウンロード済み" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "アップロードしました" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "無制限です" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "無料" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "ディスクの使用量" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "合計" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "使用中" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "/" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "トレントを追加" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "任意の cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookie" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "保存先" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "保存先" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Torrent を追加" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Torrent を開始" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "作成" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "を選択します。" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "ここにファイルをドロップ" + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "ファイル順" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL または Magnet リンク" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "URL順" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "タグ" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "オプションのベースファイルまたはTorrent のディレクトリ名" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "ベース名" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Torrent ファイルにコメントする" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "コメント" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "infohash のオプションのソース エントリ" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "情報ソース" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "非公開" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "ソース" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "作成されたトレントに追加されていません。" + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "トラッカーURL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "トラッカー" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "ベースパスとして使用" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "完了" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "一時停止" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "開始" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "停止" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "詳細" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "ファイル" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "ファイルをダウンロード" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "ファイルをダウンロード" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "ファイル詳細を読み込んでいます..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "コメント" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " の " + }, + { + "type": 1, + "value": "total" + }, + { + "type": 0, + "value": " が接続されました" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "追加しました" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "作成日" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "ダウンロード済み" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "空きディスク容量" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "ハッシュ" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "全般" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "トラッカー" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "送金" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "場所" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "なし" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "ピア" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "スケジューラ" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "無視" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "種" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "サイズ" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "タグ" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "トラッカーメッセージ" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "タイプ" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "非公開" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "公開" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "ピア" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "このトレントのピアデータはありません。" + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 選択したファイル" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 選択したファイル" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "優先度を設定" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "トラッカー" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "このトレントにはトラッカーデータがありません。" + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "タイプ" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "クライアントに接続できません。" + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "フィルタをクリア" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "ハッシュを確認" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "トレントの詳細" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "ダウンロード" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "トレントの場所を設定" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "一時停止" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "優先度" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "削除" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "タグを設定" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "トラッカーの設定" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "開始" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "停止" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "ここにファイルをドロップして追加します。" + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "表示するトレントがありません。" + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "場所を設定" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "設定中..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "ハッシュをチェック" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "データを移動" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "トレントの場所を設定" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "コメント" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "作成日" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "追加しました" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "ダウンロード速度" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "ダウンロード済み" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "空きディスク容量" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "ハッシュ" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "スケジューラを無視" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "非公開" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "名前" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "ピア" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "完了率" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "種" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "ファイルサイズ" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "タグ" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "トラッカーメッセージ" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "トラッカー" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "アップロード速度" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "アップロードしました" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Torrent を削除" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "削除してもよろしいですか? " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " Torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " Torrent" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "データを削除" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "トレントを選択していません。" + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "タグを設定" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "タグを入力" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "タグを設定" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "トラッカーの設定" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "トラッカーを入力してください" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "トラッカーの設定" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "トラッカーを読み込み中..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "並び替え" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "時間" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/ko.json b/client/src/javascript/i18n/compiled/ko.json new file mode 100644 index 000000000..ef9f5f64c --- /dev/null +++ b/client/src/javascript/i18n/compiled/ko.json @@ -0,0 +1,2621 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "토렌트 추가" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "토렌트 제거" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "토렌트 시작" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "토렌트 중지" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "설정을 저장했습니다." + } + ], + "alert.torrent.add": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "를 추가했습니다." + } + ], + "alert.torrent.add.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "를 추가하지 못했습니다." + } + ], + "alert.torrent.move": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "를 이동했습니다." + } + ], + "alert.torrent.move.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "를 이동하지 못했습니다." + } + ], + "alert.torrent.remove": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "를 제거했습니다." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "토렌트" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "를 제거하지 못했습니다." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "사용자 추가" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "관리자" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "계정 생성" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "계정 생성" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "환영합니다, Flood입니다!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "현재 사용자" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "사용자 이름은 비어있을 수 없습니다." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "로그인" + } + ], + "auth.login": [ + { + "type": 0, + "value": "로그인" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in to your account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "관리자가 아닌 사용자입니다" + } + ], + "auth.password": [ + { + "type": 0, + "value": "비밀번호" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "사용자 계정" + } + ], + "auth.username": [ + { + "type": 0, + "value": "사용자 이름" + } + ], + "button.add": [ + { + "type": 0, + "value": "추가" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "취소" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "New" + } + ], + "button.no": [ + { + "type": 0, + "value": "아니오" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "설정 저장" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Save" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "추가 중..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "예" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "연결을 검증하지 못했습니다." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "연결 문제" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent List" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Transfer History" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "다운로드 규칙 추가" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "피드 추가" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "가용 피드" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "태그 적용" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Browse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "제외" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "제외 패턴" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "설정된 피드" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "설정된 규칙" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "라벨" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "일치" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " 일치" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " 일치" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "일치 패턴" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "사용할 수 있는 피드가 없습니다." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "지정된 피드가 없습니다." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No items matching search term." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "지정된 규칙이 없습니다." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "정규 표현식" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "피드 선택" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "간격" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "시작 시 불러오기" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "다운로드 규칙" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "피드" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "토렌트 피드" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "태그" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Days" + } + ], + "feeds.time.hr": [ + { + "type": 1, + "value": "durationValue" + }, + { + "type": 0, + "value": " 시간" + } + ], + "feeds.time.min": [ + { + "type": 1, + "value": "durationValue" + }, + { + "type": 0, + "value": " 분" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "토렌트 저장 경로" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "The interval must be a positive integer." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "정규 표현식이 잘못되었습니다." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "피드를 선택해야 합니다." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "저장할 경로를 지정해야 합니다." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "라벨을 지정해야 합니다." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "올바른 피드 URL을 지정해야 합니다." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "비어있는 디렉토리입니다." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood에 이 디렉토리를 읽을 권한이 없습니다." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "없는 경로입니다. 생성될 것입니다." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "디렉토리 구조 가져오는 중..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Parent Directory" + } + ], + "filter.all": [ + { + "type": 0, + "value": "전체" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "활성" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Checking" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "완료" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "다운로드 중" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "오류" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "비활성" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "중지" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "필터: 상태" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "필터: 태그" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "필터: 트래커" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "태그 없음" + } + ], + "general.ago": [ + { + "type": 0, + "value": "전" + } + ], + "general.at": [ + { + "type": 0, + "value": "@" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "복사됨" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "복사" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "/" + } + ], + "general.to": [ + { + "type": 0, + "value": "-" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "자동" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "서버에서 미디어 정보를 가져오는 중 오류가 발생했습니다. mediainfo가 설치되어 있으며 Flood가 쓸 수 있도록 PATH가 지정되어 있는지 확인하세요." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "가져오는 중..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo 출력" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "모두 지우기" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "표시 중" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "오류 보고" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "다운로드 완료" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "다운로드하지 않음" + } + ], + "priority.high": [ + { + "type": 0, + "value": "높음" + } + ], + "priority.low": [ + { + "type": 0, + "value": "낮음" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "보통" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "About Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "전체 다운로드 슬롯 개수" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "토렌트당 다운로드 슬롯 개수" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "가용 슬롯" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "전체 업로드 슬롯 개수" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "토렌트당 업로드 슬롯 개수" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "드롭다운 메뉴 속도 목록: 다운로드" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "드롭다운 메뉴 속도 목록: 업로드" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "전체 다운로드 속도 제한" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "전체 업로드 속도 제한" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "전송 속도 제한" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "DHT 사용" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT 포트" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "탈중앙 피어 탐색" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "들어오는 연결" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "보고된 IP/호스트명" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "최대 HTTP 연결" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "피어 교환 사용" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "희망 피어 수" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "피어" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "최대 피어 수" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "최소 피어 수" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "시딩할 최대 피어 수" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "시딩할 최소 피어 수" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "포트 열기" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "임의 포트" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "포트 범위" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "완료 시 해시 검증" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "기본 다운로드 경로" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "디스크" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "최대 열어둘 파일 개수" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "메모리" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "최대 메모리 사용량" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "인증" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "대역폭" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "연결" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "설정" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "자원" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "사용자 인터페이스" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "언어" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "지역" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "표시" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "확장 보기에서는, 태그를 목록 끝에 배치하면 제일 잘 작동합니다." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "토렌트 목록 표시" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "토렌트 크기" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "간략히 보기" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "확장 보기" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "피드" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "로그아웃" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "설정" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "속도 제한" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "토렌트 검색" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "다운로드한 크기" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "업로드한 크기" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "무제한" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Free" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Used" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "total" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "connected" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "중" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "토렌트 추가" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "저장 경로" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "저장 경로" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "토렌트 추가" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "토렌트 시작" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "또는 클릭해서 파일 열기" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "파일을 여기로 끌어넣거나," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "파일" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "토렌트 URL 또는 마그넷 주소" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "By URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "토렌트" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "기본 경로로 사용" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "일시중지" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "시작" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "중지" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "속성" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "파일" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "선택한 파일 다운로드" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "선택한 파일 다운로드" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "파일 목록 불러오는 중..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "설명" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "total" + }, + { + "type": 0, + "value": " 중 " + }, + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " 연결됨" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "추가한 날" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "만든 날" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "다운로드한 크기" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "남은 디스크 공간" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "해시" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "일반" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "토렌트" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "트래커" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "전송" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "다운로드 경로" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "없음" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "피어" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "스케줄러" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "무시" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "준수" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "시드" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "크기" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "태그" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "트래커 메시지" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "종류" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "비공개" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "공개" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "미디어 정보" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "피어" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "이 토렌트에는 피어 정보가 없습니다." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 파일 선택" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 파일 선택" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "우선순위 지정" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "트래커" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "이 토렌트에는 트래커 정보가 없습니다." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "종류" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "필터 지우기" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "해시 검사" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "토렌트 속성" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "토렌트 위치 지정" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "일시중지" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "우선순위" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "제거" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "태그 지정" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "시작" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "중지" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "표시할 토렌트가 없습니다." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "경로 지정" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "이동 중..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Check hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "데이터 이동" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "토렌트 경로 지정" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "설명" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "만든 날" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "추가한 날" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "다운로드 속도" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "다운로드한 크기" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "예상 남은 시간" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "남은 디스크 공간" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "해시" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "스케줄러 무시" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "비공개" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "이름" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "완료율" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "비율" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "파일 크기" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "태그" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "트래커 메시지" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "업로드 속도" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "업로드 크기" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "토렌트 제거" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "정말 " + }, + { + "offset": 0, + "options": { + "=0": { + "value": [ + { + "type": 0, + "value": "0개 토렌트를" + } + ] + }, + "=1": { + "value": [ + { + "type": 0, + "value": "토렌트 한 개를" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": "개 토렌트를" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " 제거할까요?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "데이터 삭제" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "토렌트를 선택하지 않았습니다." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "태그 지정" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "태그 입력" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "태그 지정" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "정렬 기준" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "일" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "시" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "분" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "초" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "주" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "년" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/nl.json b/client/src/javascript/i18n/compiled/nl.json new file mode 100644 index 000000000..f7e594514 --- /dev/null +++ b/client/src/javascript/i18n/compiled/nl.json @@ -0,0 +1,2641 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Torrent toevoegen" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Verwijder Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Instellingen zijn opgeslagen." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Het toevoegen van " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " is klaar." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Het toevoegen van " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " is mislukt." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Het verplaatsen van " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " is klaar." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Het verplaatsen van " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " is mislukt." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Het verwijderen van " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " is klaar." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Het verwijderen van " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " is mislukt." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Gebruiker toevoegen" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Beheerder" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Maak account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Maak een account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Welkom bij Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Huidige gebruiker" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Wachtwoord mag niet leeg zijn." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Gebruikersnaam mag niet leeg zijn." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Wissen" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log in" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Aanmelden" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in op uw account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Gebruiker is geen Beheerder" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Wachtwoord" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Accounts" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Gebruikersnaam" + } + ], + "button.add": [ + { + "type": 0, + "value": "Toevoegen" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Annuleren" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "downloaden" + } + ], + "button.new": [ + { + "type": 0, + "value": "Nieuw" + } + ], + "button.no": [ + { + "type": 0, + "value": "Nee" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Opnieuw" + } + ], + "button.save": [ + { + "type": 0, + "value": "Instellingen opslaan" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Opslaan" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Toevoegen..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Ja" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update clientverbindingsinstellingen" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Probeer opnieuw met huidige clientverbindinginstellingen" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Kan geen verbinding maken met de client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Neem contact op met uw Flood beheerder als dit zo blijft." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Verbinding kon niet worden geverifieerd." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Klant" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Verbindingsinstellingen kunnen niet leeg zijn." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Wachtwoord" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Wachtwoord" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL naar qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Gebruikersnaam" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Gebruikersnaam" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Hostnaam" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostnaam of IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Poort" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Poort" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Pad" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Pad naar socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Type verbinding" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Het blootstellen van rTorrent via TCP kan leiden tot privilege-escalatie." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Wachtwoord" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Wachtwoord" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL naar Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Gebruikersnaam" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Gebruikersnaam" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Kan geen verbinding maken met de client. Werk de verbindingsinstellingen bij." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Probleem met verbinding" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notificaties" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent lijst" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomie" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Gegevensoverdracht geschiedenis" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Gegevensoverdracht snelheid details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Regel toevoegen" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Feed toevoegen" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Tags toevoegen" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Bekijk feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Valideer de regel door het uit te proberen. Niet opgeslagen of verzonden." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Uitsluiten" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Overslaan op patroon" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Bestaande feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Regels" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Omschrijving" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Overeenkomst" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matches" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Overeenkomst op patroon" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Geen feeds beschikbaar." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Geen feeds toegevoegd." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Geen items die overeenkomen met de zoekterm." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Geen regels toegevoegd." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "Reguliere expressie" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Zoek term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Zoek term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Selecteer feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start na toevoegen" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Regels" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Labels" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Patroon" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "dagen" + } + ], + "feeds.time.hr": [ + { + "type": 1, + "value": "durationValue" + }, + { + "type": 0, + "value": " uur" + } + ], + "feeds.time.min": [ + { + "type": 1, + "value": "durationValue" + }, + { + "type": 0, + "value": " minuten" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent downloadlocatie" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Je moet een positief nummer opgeven." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Fout in reguliere expressie." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Je moet een feed selecteren." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Je moet een downloadlocatie opgeven." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Je moet een label selecteren." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Je moet een geldige feed URL opgeven." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Lege map." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood heeft geen toestemming om deze map te lezen." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Dit pad bestaat niet. Het zal worden aangemaakt." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Er is een onbekende fout opgetreden. Probeer het opnieuw." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Ophalen van map structuur..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Bovenliggende map" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Alles" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Actief" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Controleren" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Klaar" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloaden" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Fout" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactief" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Uploaden" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Gestopt" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filter op status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filter op tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filter op tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Niet getagd" + } + ], + "general.ago": [ + { + "type": 0, + "value": "geleden" + } + ], + "general.at": [ + { + "type": 0, + "value": "bij" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Gekoppeld" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Kopiëren" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Er is een onbekende fout opgetreden" + } + ], + "general.of": [ + { + "type": 0, + "value": "van" + } + ], + "general.to": [ + { + "type": 0, + "value": "naar" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatisch" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Help mee met vertalen" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Er is een fout opgetreden tijdens het uitvoeren van mediainfo op de server. Controleer of mediainformatie is geïnstalleerd en beschikbaar is in de PATH naar Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Ophalen..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Uitvoer" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Alles wissen" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed item in wachtrij" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "Je hebt nog geen meldingen." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Weergegeven" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Fout gerapporteerd" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Downloaden voltooid" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Niet downloaden" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Hoog" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Laag" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normaal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Over Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download slots globaal" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download slots per torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slots" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload slots globaal" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload slots per torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown downloadbeperking items" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown uploadbeperking items" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Globale downloadbeperking" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Gobale uploadbeperking" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Sneldheidsbeperkingen" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Gebruik DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Poort" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Ongecentraliseerde Peer Ontdekking" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Inkomende Connecties" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Vermeld IP/Hostnaam" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximaal aantal HTTP connecties" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Gebruik Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Gewenst aantal peers" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Leechers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximaal aantal peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimaal aantal peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximaal aantal peers seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimaal aantal peers seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Open poort" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Willekeurige poort" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Poortbereik" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Tekenpunten voor schijfgebruik" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Weergeven" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Controleer hash na downloaden" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Standaard downloadlocatie" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Schijf" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximaal aantal open bestanden" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Geheugen" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Maximaal geheugengebruik" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Informatie" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authenticatie" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandbreedte" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectiviteit" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Schijf gebruik" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Instellingen" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Hulpmiddelen" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Gebruikersomgeving" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context menu-items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent detail kolommen" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Taal" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Landinstelling" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Voorkeur Label Selector" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Meerdvoudige selectie" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Enkelvoudige selectie" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Weergeven" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Ingeschakeld" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In de uitgebreide weergave werken tags het beste aan het einde van de lijst." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent Lijst Weergave" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrentgrootte" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Gecondenseerde weergave" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Uitgebreide weergave" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log uit" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Instellingen" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Sneldheidsbeperkingen" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Zoek torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOADEN" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Gedownload" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Geupload" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Ongelimiteerd" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Vrij" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Schijf gebruik" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Totaal" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Gebruikt" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "van" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Torrent toevoegen" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optionele cookie-naam=cookie-waarde" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Downloadlocatie" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Downloadlocatie" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Torrents toevoegen" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Torrent starten" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Aanmaken" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "of klik om te bladeren." + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Sleep bestanden hierheen," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Via bestand" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Via URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Labels" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optioneel basisbestand of mapnaam van de torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Basisnaam" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optioneel commentaar in torrent bestand" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Opmerking" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optionele bron item in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info bron" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privé" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Bron" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Niet toegevoegd aan gemaakte torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Gebruiken als basis pad" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Voltooid" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pauzeer" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Starten" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stoppen" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Beschrijving" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Bestanden" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download bestand" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download bestanden" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Bestand details laden..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Opmerking" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " verbonden van " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Toegevoegd op" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Aangemaakt op" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Gedownload" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Vrije schijfruimte" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Toegangssleutel" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Algemeen" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Overdracht" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Downloadlocatie" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Geen" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Leechers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Taakplanner" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Genegeerd" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Uitgevoerd" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeders" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Grootte" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Labels" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Trackerbericht" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privé" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Publiek" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Media Info" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Leechers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Er zijn geen peer-gegevens voor deze torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " bestand geselecteerd" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " bestanden geselecteerd" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Wijzig prioriteit" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Er zijn geen tracker-gegevens voor deze torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Kan geen verbinding maken met de client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Wis filters" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Controleer hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "downloaden" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Downloadlocatie instellen" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pauzeer" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Prioriteit" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Verwijder" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Tags instellen" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Trackers instellen" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Starten" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stoppen" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Sleep bestanden hierheen om ze toe te voegen." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Geen torrents om te laten zien." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Wijzig downloadlocatie" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Wijzigen..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Controleer hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Verplaats bestanden" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Wijzig torrentlocatie" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Opmerking" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Aanmaak datum" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Datum toegevoegd" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Downloadlocatie" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Downloadsnelheid" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Gedownload" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Vrije schijfruimte" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Toegangssleutel" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Negeer Taakplanner" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privé" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Naam" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Leechers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percentage compleet" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Verhouding" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeders" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Bestandsgrootte" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Labels" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker bericht" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Uploadsnelheid" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Geupload" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Torrents verwijderen" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Weet je zeker dat je " + }, + { + "offset": 0, + "options": { + "=0": { + "value": [ + { + "type": 0, + "value": "geen torrents" + } + ] + }, + "=1": { + "value": [ + { + "type": 0, + "value": "één torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " wilt verwijderen?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Gegevens verwijderen" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Je hebt geen torrents geselecteerd." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Tags instellen" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Tags invoeren" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Tags instellen" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Trackers instellen" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Voer een tracker in" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Trackers instellen" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Nummers laden..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sorteer op" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "KB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "Mb" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "D" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "h" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "min" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "w" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "j" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/no.json b/client/src/javascript/i18n/compiled/no.json new file mode 100644 index 000000000..1dd653f55 --- /dev/null +++ b/client/src/javascript/i18n/compiled/no.json @@ -0,0 +1,2600 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Legg til torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Fjern torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stopp torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Innstillingene ble lagret." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Helt lagt til " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenter" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Kunne ikke legge til " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenter" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenter" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Kunne ikke flytte " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenter" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Fjernet " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenter" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Kunne ikke fjerne " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenter" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Legg til bruker" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Administrator" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Opprett konto" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Opprett en konto" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Velkommen til flommet!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Gjeldende bruker" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Passord kan ikke være tomt." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Brukernavnfeltet kan ikke være tomt." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Logg inn" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Innlogging" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Logg inn på din konto." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Brukeren er ikke Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Passord" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Bruker kontoer" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Brukernavn" + } + ], + "button.add": [ + { + "type": 0, + "value": "Legg til" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Avbryt" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Nedlasting" + } + ], + "button.new": [ + { + "type": 0, + "value": "Ny" + } + ], + "button.no": [ + { + "type": 0, + "value": "Nei" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Lagre innstillinger" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Lagre" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Legge til..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Ja" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Kan ikke koble til klienten" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Tilkobling kunne ikke verifiseres." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Klient" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Tilkoblingsinnstillinger kan ikke være tomt." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Passord" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Passord" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "Nettadresse" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL-adresse til qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Brukernavn" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Brukernavn" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Vert" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Vertsnavn eller IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Sti" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Sti til stien" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Tilkobling type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Kan ikke koble til klienten. Oppdater tilkoblingsinnstillingene." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Tilkobling problem" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Varsler" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Liste over torrent" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent taksonomi" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data overførings historikk" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Detaljer for overføring av data" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Legg til regler for nedlasting" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Legg til strøm" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Gjeldende Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Legg til etiketter" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Bla gjennom fôr" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Ekskluder" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Utelat Mønster" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Eksisterende Feeder" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Eksisterende regler" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Intervall" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Etikett" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Kamp" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " treff" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matcher" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Matchende mønster" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Ingen fôr tilgjengelig." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Ingen feeds definert." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Ingen elementer samsvarende med søkebegrep." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Ingen regler definert." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEks" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Søk etter uttrykk" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Søk etter uttrykk" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Velg mating" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Intervall" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start ved lasting" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Last ned regler" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Strøm" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeder" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tagger" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test samsvarsmønster" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Dager" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Timer" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutter" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent destinasjon" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "Nettadresse" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Intervallet må være et positivt heltall." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Ugyldig regulært uttrykk." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Du må velge en mating." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Du må spesifisere en destinasjon." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Du må angi en etikett." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Du må angi en gyldig feed URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Tom mappe." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flom har ikke tillatelse til å lese denne mappen." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Denne stien finnes ikke. Den vil bli opprettet." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Det oppstod en ukjent feil. Prøv på nytt." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Henter mappestruktur char@@0" + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Overordnet mappe" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Alle" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Aktiv" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Kontrollerer" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Laster ned" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Feil" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inaktiv" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Deler" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stoppet" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtrer etter status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtrer etter tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtrer etter tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Umerket" + } + ], + "general.ago": [ + { + "type": 0, + "value": "siden" + } + ], + "general.at": [ + { + "type": 0, + "value": "på" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Kopiert" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Kopier" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "En ukjent feil har oppstått" + } + ], + "general.of": [ + { + "type": 0, + "value": "av" + } + ], + "general.to": [ + { + "type": 0, + "value": "til" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatisk" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Det oppstod en feil under kjøring av mediainfo på serveren. Sjekk at mediainfo er installert og tilgjengelig i PATH flom." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Henter..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo utdata" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Fjern alle" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Strøm enhet i kø" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Viser" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Feil rapportert" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Nedlasting ferdig" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Ikke last ned" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Høy" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Lav" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Vanlig" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Om Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Last ned slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Last ned slot per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Spor tilgjengelighet" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Last opp plasser globalt" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Last opp plasser per torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Forhåndsinnstillinger for rulleliste: Last ned" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Forhåndsinnstillinger for rulleliste: Last opp" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global nedlastingshastighet Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global opplastingshastighet Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Overfør bokstaver" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Aktiver DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Desentralisert Peer Oppdagelse" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Innkommende tilkoblinger" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Rapportert IP/Vertsnavn" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maksimal HTTP-tilkobling (Automatic Translation)" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Aktiver Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Mottakere utsendt" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Likemenn" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maksimalt antall kilder" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimum klienter" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maks spredning til klienter" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum kilder for å dele" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Åpne port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Tilfeldig port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Rekkefølge" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Bruk diskens ridningspoeng" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Vis" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verifiser Hash ved ferdigstillelse" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Standard nedlastingsmappe" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maksimalt antall åpne filer" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Minne" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Maks minnebruk" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Om" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Autentisering" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Båndbredde" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Tilkobling" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Bruk av diskplass" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Innstillinger" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Ressurser" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Brukers grensesnitt" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Sammenhengende menyelementer" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detalj kolonner" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Språk" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Språk" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Vis" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Aktivert" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "I den utvidede visningen, fungerer merkelapper best på slutten av listen." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Vis torrent liste" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent størrelse" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Kondensert visning" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Utvidet visning" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Strøm" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Logg ut" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Innstillinger" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Grenseverdier for hastighet" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Søk torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "LAST NED" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "LAST OPP" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Nedlastet" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Opplastet" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Ubegrenset" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Gratis" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Bruk av diskplass" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Totalt" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Brukt" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "av" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Legg til torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Valgfri cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Informasjonskapsler" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Mål" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Mål" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Legge til torrenter" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Opprett" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "eller klikk for å bla gjennom" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Slipp noen filer her," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Etter fil" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL eller Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Etter URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tagger" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrenter" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Valgfri basisfil eller mappenavn på torrenten" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Navn på database" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Valgfri kommentar i torrentfil" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Valgfri kildeoppføring i infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info kilde" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Kilde" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Etiketter i flommen. Ikke lagt til i opprettet torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Sporingsagent URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Sporere" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Bruk som basis sti" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Fullført" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Begynn" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stopp" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Detaljer" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Filer" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Last ned filen" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Last ned filer" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Laster fildetaljer..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " koblet til " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Lagt" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Opprettet dato" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Nedlastet" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Ledig diskplass" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Generelt" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Sporingsagent" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Overfør" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Sted" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Ingen" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Likemenn" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Oppgaveplanlegging" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignorert" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Uhørt" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Frø" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Størrelse" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tagger" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Melding om sporingsagent" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type:" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Offentlig" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Likemenn" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Det er ingen kilder data for denne torren." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " valgt fil" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " valgte filer" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Angi prioritet" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Sporere" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Det finnes ingen data for tracker for denne torren." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type:" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Kan ikke koble til annonsøren." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Fjern filtre" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Sjekk Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent detaljer" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Nedlasting" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Angi torrent plassering" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Prioritet" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Fjern" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Angi Etiketter" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Angi trackere" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Begynn" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stopp" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Slipp filer her for å legge dem til." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Ingen torrenter å vise." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Angi sted" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Innstillinger..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Sjekk hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Flytt data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Angi torrent plassering" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Opprettet dato" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Lagt" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Nedlastings hastighet" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Nedlastet" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Ledig diskplass" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignorer planlegger" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Navn" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Likemenn" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Prosent fullført" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Forhold" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Frø" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Fil Størrelse" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tagger" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Melding om sporingsagent" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Sporere" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Opplastnings hastighet" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Opplastet" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Fjern torrenter" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Er du sikker på at du vil fjerne " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrenter" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Slett data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Du har ikke valgt noen torrenter." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Angi Etiketter" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Angi tagger" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Angi Etiketter" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Angi trackere" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Angi en tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Angi trackere" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Laster sporere..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sorter etter" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "NO" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "Mb" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "D" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "t" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "min" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "S" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "uke" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/pl.json b/client/src/javascript/i18n/compiled/pl.json new file mode 100644 index 000000000..17da20d41 --- /dev/null +++ b/client/src/javascript/i18n/compiled/pl.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Dodaj torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Usuń torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Uruchom torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Zatrzymaj torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Zapisano ustawienia." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Pomyślnie dodano " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Nie udało się dodać " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Pomyślnie przeniesiono " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Nie udało się przenieść " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrentów" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrentów" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Pomyślnie usunięto " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrenty" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Nie udało się usunąć " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrentów" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrentów" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Dodaj użytkownika" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Administrator" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Utwórz konto" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Utwórz konto" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Witaj w powodzie!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Bieżący użytkownik" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Hasło nie może być puste." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Nazwa użytkownika nie może być pusta." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Zaloguj się" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Logowanie" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Zaloguj się do swojego konta." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Użytkownik nie jest administratorem" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Hasło" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Konta użytkowników" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Nazwa użytkownika" + } + ], + "button.add": [ + { + "type": 0, + "value": "Dodaj" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Anuluj" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Pobierz" + } + ], + "button.new": [ + { + "type": 0, + "value": "Nowy" + } + ], + "button.no": [ + { + "type": 0, + "value": "Nie" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Zapisz ustawienia" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Zapisz" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Dodawanie..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Tak" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Nie można połączyć się z klientem" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Nie można zweryfikować połączenia." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Klient" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Ustawienia połączenia nie mogą być puste." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Hasło" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Hasło" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "Adres URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL do qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Nazwa użytkownika" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Nazwa użytkownika" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "Rtorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Nazwa hosta lub IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Ścieżka" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Ścieżka do gniazda" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Typ połączenia" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Nie można połączyć się z klientem. Proszę zaktualizować ustawienia połączenia." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Problem z połączeniem" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Powiadomienia" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Lista torrentów" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Taksonomia torrent" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Historia transferu danych" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Szczegóły tempa transferu danych" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Dodaj regułę pobierania" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Dodaj kanał" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Stosowany kanał" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Zastosuj tagi" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Przeglądaj kanały" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Wyklucz" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Wyklucz wzór" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Istniejące kanały" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Istniejące reguły" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interwał" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Etykieta" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Dopasowanie" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " pasuje do" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " pasuje do" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Wzorzec dopasowania" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Brak dostępnych kanałów." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Nie zdefiniowano kanałów." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Brak elementów pasujących do wyszukiwarki." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Nie zdefiniowano reguł." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Szukaj frazy" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Szukaj frazy" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Wybierz kanał" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interwał" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Zacznij od załadowania" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Zasady pobierania" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Kanały" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Kanały torrent" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tagi" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Testowy wzór dopasowania" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Dni" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Godziny" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Protokoły" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Przeznaczenie torrentów" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "Adres URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Interwał musi być dodatnią liczbą całkowitą." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Nieprawidłowe wyrażenie regularne." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Musisz wybrać kanał." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Musisz określić miejsce docelowe." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Musisz określić etykietę." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Musisz podać poprawny adres URL kanału." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Pusty katalog." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Powódź nie ma uprawnień do odczytu tego katalogu." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Ta ścieżka nie istnieje. Zostanie utworzona." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Wystąpił nieznany błąd. Spróbuj ponownie." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Pobieranie struktury katalogu..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Katalog nadrzędny" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Wszystkie" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Aktywne" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Sprawdzanie" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Pobieranie" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Błąd" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Nieaktywny" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Nasiona" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Zatrzymano" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtruj według statusu" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtruj według tagu" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtruj według trackera" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Nietagowane" + } + ], + "general.ago": [ + { + "type": 0, + "value": "temu" + } + ], + "general.at": [ + { + "type": 0, + "value": "w" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Skopiowano" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Kopiuj" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Wystąpił nieznany błąd" + } + ], + "general.of": [ + { + "type": 0, + "value": "z" + } + ], + "general.to": [ + { + "type": 0, + "value": "do" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatyczne" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Wystąpił błąd podczas uruchamiania mediainfo na serwerze. Sprawdź, czy mediainfo jest zainstalowany i dostępny w PATH do powodzi." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Pobieranie..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Wyjście Mediainfo" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Wyczyść wszystko" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Element RSS w kolejce" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Wyświetlanie" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Błąd zgłoszony" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Pobieranie zakończone" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Nie pobieraj" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Wysoka" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Niski" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normalny" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "O powodzie" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Pobierz gniazda globalne" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Pobierz miejsca na torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Dostępność miejsca" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Prześlij Sloty globalne" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Prześlij Sloty na torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Rozwijane ustawienia: Pobierz" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Rozwijane ustawienia: Załaduj" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Globalna przepustnica szybkości pobierania" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Globalna przepustnica Szybkości Wysyłania" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Prędkość transferu przepustnicy" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Włącz DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "Port DHT" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Odkrycie zdecentralizowanego peera" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Połączenia przychodzące" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Zgłoszone IP/nazwa hosta" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maksymalna liczba połączeń HTTP" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Włącz wymianę peer" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Pożądane rówieśniki" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Uczestnicy" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maksymalna liczba peerów" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimalna liczba peerów" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maksymalna ilość seedów peerów" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimalne seedowanie peerów" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Otwórz port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Losowy port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Zakres portu" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Punkty montowania dysku" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Pokaż" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Zweryfikuj skrót po zakończeniu" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Domyślny katalog pobierania" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Dysk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maksymalna liczba otwartych plików" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Pamięć" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Maksymalne zużycie pamięci" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "O programie" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Uwierzytelnianie" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Przepustowość" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Łączność" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Użycie dysku" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Ustawienia" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Zasoby" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Interfejs użytkownika" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Pozycje menu kontekstowego" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Kolumny szczegółów torrenta" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Język" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Język" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Pokaż" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Włączone" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "W rozszerzonym widoku tagi działają najlepiej na końcu listy." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Wyświetlanie listy torrentów" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Rozmiar torrenta" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Skondensowany widok" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Widok rozszerzony" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Kanały" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Wyloguj" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Ustawienia" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Ograniczenia prędkości" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Szukaj torrentów" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "POBIERZ" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "PRZEŚLIJ" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Pobrano" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Przesłano" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Nieograniczona" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Darmowe" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Użycie dysku" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Łącznie" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Używane" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "z" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Dodaj torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Opcjonalnie nazwa ciasteczka=wartość ciasteczka" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Pliki cookie" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Miejsce przeznaczenia" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Miejsce przeznaczenia" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Dodaj torrenty" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Uruchom torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Utwórz" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "lub kliknij, aby przeglądać" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Upuść pliki tutaj," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Wg pliku" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "URL torrenta lub Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Według adresu URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tagi" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrenty" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Opcjonalny plik podstawowy lub nazwa katalogu torrenta" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Nazwa podstawowa" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Opcjonalny komentarz w pliku torrent" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Komentarz" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Opcjonalny wpis źródłowy w infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Źródło informacji" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Prywatny" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Źródło" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tagi w powodzie. Nie dodano do utworzonego torrentu." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "URL trackera" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Śledzenie" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Użyj jako ścieżki bazowej" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Zakończone" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Wstrzymaj" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Rozpocznij" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Zatrzymaj" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Szczegóły" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Pliki" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Pobierz plik" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Pobierz pliki" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Ładowanie szczegółów pliku..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Komentarz" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " połączony z " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Dodano" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Data utworzenia" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Pobrano" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Wolne miejsce na dysku" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Ogólny" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Śledzenie" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Przelew" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Lokalizacja" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Brak" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Uczestnicy" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Harmonogram" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignorowane" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "obfite" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Nasiona" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Rozmiar" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tagi" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Wiadomość trackera" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Typ" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Prywatny" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Publiczne" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Uczestnicy" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Brak danych peerów dla tego torrentu." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " wybrany plik" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " wybrano pliki" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Ustaw priorytet" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Śledzenie" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Brak danych trackera dla tego torrentu." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Typ" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Nie można połączyć się z klientem." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Wyczyść filtry" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Sprawdź hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Szczegóły torrenta" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Pobierz" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Ustaw lokalizację torrenta" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Wstrzymaj" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priorytet" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Usuń" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Ustaw tagi" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Ustaw trackery" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Rozpocznij" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Zatrzymaj" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Upuść pliki tutaj, aby je dodać." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Brak torrentów do wyświetlenia." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Ustaw lokalizację" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Ustawienie..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Sprawdź skrót" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Przenieś dane" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Ustaw lokalizację torrenta" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Komentarz" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Data utworzenia" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Dodano" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Prędkość pobierania" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Pobrano" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Wolne miejsce na dysku" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignoruj harmonogram" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Prywatny" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Nazwisko" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Uczestnicy" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Procent zakończony" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Stosunek" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Nasiona" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Rozmiar pliku" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tagi" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Wiadomość trackera" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Śledzenie" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Prędkość przesyłania" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Przesłano" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Usuń torrenty" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Czy na pewno chcesz usunąć " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrenty" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrentów" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Usuń dane" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Nie wybrałeś żadnych torrentów." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Ustaw tagi" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Wprowadź tagi" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Ustaw tagi" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Ustaw trackery" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Wprowadź tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Ustaw trackery" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Ładowanie trackerów..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sortuj wg" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "ob" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "godz." + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "u rot ob" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "tyg." + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/pt.json b/client/src/javascript/i18n/compiled/pt.json new file mode 100644 index 000000000..fe30bb379 --- /dev/null +++ b/client/src/javascript/i18n/compiled/pt.json @@ -0,0 +1,2624 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Adicionar Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Remover Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Iniciar Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Parar Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Configurações guardas com sucesso." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Adicionado com sucesso " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Falha ao adicionar " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Movido com sucesso " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Falha ao mover " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Removido com sucesso " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent removido com sucesso" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Falha ao remover " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Adicionar Utilizador" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Administrador" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Criar conta" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Criar uma conta" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Bem-vindo ao Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Utilizador atual" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "A senha não pode estar vazia." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Nome de utilizador não pode estar vazio." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Iniciar sessão" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Iniciar sessão" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Inicie sessão na sua conta." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Utilizador não é Administrador" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Palavra-passe" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Contas dos utilizadores" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Nome de Utilizador" + } + ], + "button.add": [ + { + "type": 0, + "value": "Adicionar" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancelar" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Transferir" + } + ], + "button.new": [ + { + "type": 0, + "value": "Novidades" + } + ], + "button.no": [ + { + "type": 0, + "value": "Não" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Guardar Configurações" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Guardar" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Adicionando..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Sim" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Não é possível conectar-se ao cliente" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "A conexão não pôde ser verificada." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Cliente" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Configurações de conexão não podem estar vazias." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "Bittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Palavra-passe" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Palavra-passe" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL:" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL para qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Usuário:" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Usuário:" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Servidor" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Nome do host ou IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Porta" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Porta" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Caminho" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Caminho para o socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Tipo de conexão" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Não é possível conectar-se ao cliente. Por favor atualize as configurações de conexão." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Problema de conectividade" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notificações" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Lista de Torrents" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Taxonomia do Torrent" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Histórico de Transferências de Dados" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Detalhes da Taxa de Transferência de Dados" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Adicionar regra de download" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Adicionar Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Feed aplicável" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Aplicar Etiquetas" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Procurar feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Excluir" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Excluir padrão" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Feeds existentes" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Regras existentes" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Intervalo" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Etiqueta" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Corresponder" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Padrão Correspondente" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Não há feeds disponíveis." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Não foi definido nenhum feed." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Nenhum item corresponde ao termo de pesquisa." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Nenhuma regra definida." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Termo de pesquisa" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Termo de pesquisa" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Selecionar Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Intervalo" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Iniciar ao carregar" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Regras de Transferência" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Feeds de Torrent" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Etiquetas" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Testar Padrão Correspondente" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Dias" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Horas" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutos" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Destino do Torrent" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "O intervalo deve ser um número inteiro positivo." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Expressão regular inválida." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Precisa selecionar um feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Precisa especificar um destino." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Precisa especificar uma etiqueta." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Precisa especificar um URL de feed válido." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Diretório vazio." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "O Flood não tem permissão para aceder a este diretório." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Este caminho não existe. Vai ser criado." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Ocorreu um erro desconhecido. Por favor, tente novamente." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Obtendo a estrutura de diretórios..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Diretório Superior" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Todos" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Ativo" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Verificando" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Concluído" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Transferindo" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Erro" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inativo" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Semeando" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Parado" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtrar por status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtrar por Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtrar por Rastreador" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Sem etiqueta" + } + ], + "general.ago": [ + { + "type": 0, + "value": "atrás" + } + ], + "general.at": [ + { + "type": 0, + "value": "em" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copiado" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copiar" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Ocorreu um erro desconhecido" + } + ], + "general.of": [ + { + "type": 0, + "value": "de" + } + ], + "general.to": [ + { + "type": 0, + "value": "para" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automático" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Ocorreu um erro durante a execução de mediainfo no servidor. Verifique se a mediainfo está instalada e disponível no PATH para Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Buscando..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Saída Mediainfo" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Limpar Tudo" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Item do Feed na fila" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Mostrando" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Erro relatado" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Download concluído" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Não baixar" + } + ], + "priority.high": [ + { + "type": 0, + "value": "alta" + } + ], + "priority.low": [ + { + "type": 0, + "value": "baixa" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Sobre Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Baixar Slots Globais" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Baixar Slots por Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Disponibilidade de Slot" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Carregar Slots Globais" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Carregar Slots por Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Predefinições Dropdown: Baixar" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Predefinições do Dropdown: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Taxa de Download Global" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Taxa de envio global" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Limite da Taxa de Transferência" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Habilitar DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "Porta DHT" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Descoberta Descentralizada de Pares" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Conexões de entrada" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "IP/Hostname reportado" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Conexões HTTP máximas" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Habilitar Troca de Pares" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Pares Desejados" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Pares" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Máximo de Pares" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Mínimo de Pares" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Semente Máxima de Pares" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Mínimo de Semeamento de Pares" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Porta Aberta" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Randomizar Porta" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Intervalo de Portas" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Pontos de Uso de Disco" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Apresentar" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verificar o Hash na Conclusão" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Diretório de download padrão" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disco" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Máximo de Arquivos Abertos" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memória" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Uso máximo de memória" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "SOBRE" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Autenticação" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Banda" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Conectividade" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Uso do disco" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Confirgurações" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Recursos" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Interface do usuário" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Itens do Menu de Contexto" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Colunas de Detalhes do Torrent" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "IDIOMA" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Localidade" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Apresentar" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Ativado" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "Na exibição expandida, as tags funcionam melhor no final da lista." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Exibição de Lista de Torrent" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Tamanho do Torrent" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Vista Condensada" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Vista Expandida" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Conteúdos" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Encerrar Sessão" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Confirgurações" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Limite de Velocidade" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Procurar torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "TRANSFERIR" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "ENVIAR" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Baixado" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Enviado" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Ilimitado" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Livre" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Uso do disco" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Utilizado" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "de" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Adicionar Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Nome opcional do cookie-=valor-cookie" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Biscoitos" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destino" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destino" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Adicionar Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Iniciar Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Crio" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "ou clique para navegar" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Solte alguns arquivos aqui," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Por Arquivo" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "URL do Torrent ou Link Magnet" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Por URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Etiquetas" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrentes" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Arquivo base opcional ou nome do diretório do torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Nome Base" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Comentário opcional no arquivo torrent" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comentar" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Entrada de origem opcional em infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Fonte da Informação" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privado" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "fonte" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags em Flood. Não adicionado ao torrent criado." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "URL do Rastreador" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Rastreadores" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Usar como caminho de base" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Concluído" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Suspender" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Iniciar" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Interromper" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "detalhes" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "arquivos" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Baixe o arquivo" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Baixe arquivos" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Carregando detalhes do arquivo..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comentar" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " conectado a " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Adicionado" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Data de Criação" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Baixado" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Espaço em disco livre" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Gerais" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Rastreador" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transferência" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Local:" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Nenhuma" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Pares" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Agendar" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignorado" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obedecido" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Sementes" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Tamanho" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Etiquetas" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Mensagem do Rastreador" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "tipo" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privado" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Público" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Informações de mídia" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Pares" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Não há dados de par para este torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " arquivo selecionado" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " arquivos selecionados" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Definir prioridade" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Rastreadores" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Não há dados do rastreador para este torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "tipo" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Não é possível conectar-se ao cliente." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Limpar Filtros" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Verificar o Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Detalhes do Torrent" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Transferir" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Definir local do Torrent" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Suspender" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Prioridade" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Excluir" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Definir Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Definir Rastreadores" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Iniciar" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Interromper" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Arraste arquivos aqui para adicioná-los." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Não há torrents para exibir." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Definir Local" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Configuração..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Verificar o hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Mover dados" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Definir local do Torrent" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comentar" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Data de Criação" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Adicionado" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Velocidade de Download" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Baixado" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "Tempo Estimado" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Espaço em disco livre" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignorar o Agendador" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privado" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Nome:" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Pares" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Porcentagem Completa" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Tarifa" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Sementes" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Tamanho do arquivo" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Etiquetas" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Mensagem do Rastreador" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Rastreadores" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Velocidade de Upload" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Enviado" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Remover Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Você tem certeza que quer remover " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Apagar dados" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Você não selecionou nenhum torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Definir Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Insira tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Definir Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Definir Rastreadores" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Digite um rastreador" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Definir Rastreadores" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Carregando rastreadores..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Classificar por" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "BR" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "KB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "Tb" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "HR" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "min" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "Semana" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "a" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/ro.json b/client/src/javascript/i18n/compiled/ro.json new file mode 100644 index 000000000..b31b1473f --- /dev/null +++ b/client/src/javascript/i18n/compiled/ro.json @@ -0,0 +1,2784 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Adaugă torent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Șterge torentul" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Pornește torentul" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Oprește torentul" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Setări salvate cu succes." + } + ], + "alert.torrent.add": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "A fost adăugat" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Au fost adăugate" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " cu succes " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torente" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Nu " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "s-a" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "s-au" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " putut adăuga " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torente" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "A fost mutat" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Au fost mutate" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " cu succes " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torente" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Nu " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "s-a" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "s-au" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " mutat " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torente" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "A fost șters" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Au fost șterse" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " cu succes " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torente" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Nu " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "s-a" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "s-au" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": " șters " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torente" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Adaugă utilizator" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Creează cont" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Creează un cont" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Bine ai venit la Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Utilizator curent" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Parola nu poate fi goală." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Numele de utilizator nu poate fi gol." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Șterge" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Autentificare" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Autentificare" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Conectează-te la contul tău." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Utilizatorul nu este Administrator" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Parolă" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Conturi de utilizator" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Nume de utilizator" + } + ], + "button.add": [ + { + "type": 0, + "value": "Adaugă" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Anulează" + } + ], + "button.close": [ + { + "type": 0, + "value": "Închide" + } + ], + "button.download": [ + { + "type": 0, + "value": "Descarcă" + } + ], + "button.new": [ + { + "type": 0, + "value": "Nou" + } + ], + "button.no": [ + { + "type": 0, + "value": "Nu" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Încearcă din nou" + } + ], + "button.save": [ + { + "type": 0, + "value": "Salvează setările" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Salvează" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Se adaugă..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Da" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Actualizează setările de conexiune ale clientului" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Încearcă din nou cu setările curente ale clientului" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Nu se poate conecta la client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Te rog contactează administratorul tău Flood dacă această eroare persistă." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Conexiunea nu a putut fi verificată." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Setările conexiunii nu pot fi goale." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Parolă" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Parolă" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "Adresă" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "Adresă către qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Nume" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Nume" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Nume host sau IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Cale" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Calea către socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Tipul conexiunii" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Expunerea rTorrent prin TCP poate permite creșterea privilegiilor." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Parolă" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Parolă" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "Adresă" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "Adresă către interfața Transmission RPC" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Utilizator" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Utilizator" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Nu se poate conecta la client. Actualizează setările conexiunii." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Problemă de conectivitate" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notificări" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Lista de torente" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Taxonomia torentului" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Istoricul transferurilor de date" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Detaliile ratei de transfer ale datelor" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Adaugă regulă de descărcare" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Adaugă flux" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Flux aplicabil" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Aplică etichete" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Răsfoiește fluxuri" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validează regula încercând-o. Nu a fost salvată sau trimisă." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclude" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exclude modelul" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Fluxuri existente" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Reguli existente" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Etichetă" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Potrivire" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " potrivire" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " potriviri" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Potrivire model" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Nici un flux disponibil." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Nici un flux definit." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Nu există elemente care să corespundă termenului de căutare." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Nici o regulă definită." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Termen de căutare" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Termen de căutare" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Selectează flux" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Începe la încărcare" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Reguli de descărcare" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Fluxuri" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Fluxuri de torent" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Etichete" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Testează modelul de potrivire" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Zile" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Ore" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minute" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Destinația torentului" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "Adresă" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Intervalul trebuie să fie un număr întreg pozitiv." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Expresie regulată invalidă." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Trebuie să selectezi un flux." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Trebuie să specifici o destinație." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Trebuie să specifici o etichetă." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Trebuie să specifici o adresă de flux validă." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Dosar gol." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood nu are permisiunea de a citi acest director." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Această cale nu există. Va fi creată." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "A apărut o eroare necunoscută. Te rugăm să încerci din nou." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Se preia structura directorului..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Director părinte" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Toate" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Activ" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Se verifică" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complet" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Descărcând" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Eroare" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactiv" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Donând" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Oprit" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtrare după stare" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtrare după etichetă" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtrează după tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Neetichetat" + } + ], + "general.ago": [ + { + "type": 0, + "value": "acum" + } + ], + "general.at": [ + { + "type": 0, + "value": "la" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copiat" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copie" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "A apărut o eroare necunoscută" + } + ], + "general.of": [ + { + "type": 0, + "value": "din" + } + ], + "general.to": [ + { + "type": 0, + "value": "către" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automat" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Ajută-ne să traducem" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "A apărut o eroare în timpul executării mediainfo pe server. Verificați dacă mediainfo este instalat și disponibil în CALEA către Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Se preia..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Ieșire mediainfo" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Șterge tot" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Element din flux în așteptare" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "Nicio notificare de afișat." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Afișare" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Eroare raportată" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Descărcare finalizată" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Nu descărca" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Ridicată" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Scazută" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normală" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Despre Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Sloturi globale pentru descărcare" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Sloturi pentru descărcare per torent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Disponibilitatea sloturilor" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Sloturi globale pentru încărcare" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Sloturi pentru încărcare per torent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Presetări dropdown: Descărcare" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Presetări dropdown: Încărcare" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Rata globală de descărcare" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Rata globală de încărcare" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Rata de transfer" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Activează DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "Port DHT" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Descoperirea descentralizată a partenerilor" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Conexiuni de intrare" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "IP/Hostname raportat" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Conexiuni HTTP maxime" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Activează schimbul de parteneri" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Parteneri dornici" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Parteneri" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Nr. maxim de parteneri" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Nr. minim de parteneri" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Nr. maxim de parteneri donatori" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Nr. minim de parteneri donatori" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Port deschis" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Port aleator" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Interval port" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Puncte de montare pentru utilizarea discului" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Arată" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verifică hash-ul la finalizare" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Directorul implicit pentru descărcări" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disc" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Nr. maxim de fişiere deschise" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memorie" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Utilizarea maximă a memoriei" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Despre" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Autentificare" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Lățime de bandă" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Conectivitate" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Utilizare disc" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Setări" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resurse" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Interfața cu utilizatorul" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Elementele meniu contextual" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Coloane cu detaliile torentelor" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Limba" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Localizare" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Preferința selectorului de etichete" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Selecție multiplă" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Selecție unică" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Arată" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Activat" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "În vederea extinsă, etichetele funcționează cel mai bine la sfârșitul listei." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Afișare listă torente" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Mărimea torentului" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Vedere compactă" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Vedere extinsă" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Fluxuri" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Ieșire din cont" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Setări" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Limite de viteză" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Temă întunecată" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Temă luminată" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Caută torente" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DESCĂRCARE" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "ÎNCĂRCARE" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Descărcat" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Încărcat" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Nelimitat" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Liber" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Utilizare disc" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Utilizat" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "din" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Adaugă torent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "cookie-name = cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookie-uri" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destinație" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destinație" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Adaugă torente" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Pornește torentul" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Crează" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "sau fă clic pentru a naviga" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Pune câteva fișiere aici," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "După fișier" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Adresa torentului sau adresa magnet" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "După adresă" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Etichete" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torente" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Numele fișierului de bază sau numele directorului torentului" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Nume de bază" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Comentariu în fișierul torent" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comentariu" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Intrare sursă în infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Sursa de informații" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Sursa" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Etichete în Flood. Nu se adăugă la torentul creat." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Adresă tracker" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackere" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Folosește ca și cale de bază" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Finalizat" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Întrerupe" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Pornește" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Oprește" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Detalii" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Fișiere" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Descarcă fişierul" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Descarcă fişierele" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Se încarcă detaliile fișierului..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comentariu" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " conectați din " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Adăugat" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Data creării" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Descărcat" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Spațiu liber pe disc" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Generalități" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfer" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Locație" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Niciunul" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Parteneri" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Planificator" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignorat" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Respectat" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Parteneri" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Dimensiune" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Etichete" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Mesaj tracker" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Tip" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Parteneri" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Nu există date despre parteneri pentru acest torent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " fișier selectat" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " fișiere selectate" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Setează prioritate" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackere" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Nu există date despre tracker pentru acest torent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Tip" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generează adresă magnet" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Se încarcă trackerele..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Adresă magnet" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Adresă magnet cu trackere" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "Acesta este un torent privat." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Nu se poate conecta la client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Șterge filtrele" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Verifică hash-ul" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Detalii torent" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Descarcă" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generează adresă magnet" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Setează locația torentului" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Întrerupe" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Prioritate" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Șterge" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Setează etichete" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Setează trackere" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Pornește" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Oprește" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Plasați fișierele aici pentru a le adăuga." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Nu sunt torente de afișat." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Setează locația" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Se setează..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Verifică hash-ul" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Mută datele" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Setează locația torentului" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comentariu" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Data creării" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Adăugat" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Locație" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Viteza de descărcare" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Descărcat" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "Timp rămas" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Spațiu liber pe disc" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignoră planificatorul" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Nume" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Parteneri" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Procentaj completare" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Raport" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Donatori" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Dimensiune fișier" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Etichete" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Mesajul trackerului" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackere" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Viteza de încărcare" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Încărcat" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Șterge torentele" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Ești sigur că vrei să ștergi " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrente" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Ștergere datele" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Nu ai selectat niciun torent." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Setează etichete" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Introduceți etichetele" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Setare etichete" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Setează trackere" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Introdu un tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Setare trackere" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Se încarcă trackerele..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sortează după" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "oră" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/ru.json b/client/src/javascript/i18n/compiled/ru.json new file mode 100644 index 000000000..80889a33e --- /dev/null +++ b/client/src/javascript/i18n/compiled/ru.json @@ -0,0 +1,2589 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Добавить торрент" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Удалить торрент" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Начать торрент" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Остановить торрент" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Настройки успешно сохранены." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Успешно добавлено " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрентов" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Не удалось добавить " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрентов" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Успешно перемещено " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрентов" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Не удалось переместить " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрентов" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Удалено " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрентов" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Не удалось удалить " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрентов" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Добавить пользователя" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Админ" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Создать Аккаунт" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Создать аккаунт" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Добро пожаловать в Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Текущий пользователь" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Пароль не может быть пустым." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Имя пользователя не может быть пустым." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Войти" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Логин" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Войдите в свою учетную запись." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Пользователь не является администратором" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Пароль" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Учетные записи пользователей" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Имя пользователя" + } + ], + "button.add": [ + { + "type": 0, + "value": "Добавить" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Отмена" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Скачать" + } + ], + "button.new": [ + { + "type": 0, + "value": "Новый" + } + ], + "button.no": [ + { + "type": 0, + "value": "Нет" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Сохранить настройки" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Сохранить" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Добавление..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Да" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Не удается подключиться к клиенту" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Соединение не может быть проверено." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Клиент" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Настройки соединения не могут быть пустыми." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Пароль" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Пароль" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Имя пользователя" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Имя пользователя" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "рторрент" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Хост" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Имя хоста или IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Порт" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Порт" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Путь" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Путь к сокету" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Тип соединения" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Не удается подключиться к клиенту. Пожалуйста, обновите настройки соединения." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Проблема с подключением" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Уведомления" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Список торрентов" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Таксономия торрента" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "История передачи данных" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Данные по скорости передачи данных" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Добавить правило загрузки" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Добавить канал" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Применимая лента" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Применить теги" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Просмотр каналов" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Исключить" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Исключить шаблон" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Существующие каналы" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Существующие правила" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Интервал" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Метка" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Матч" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " совпадает" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " совпадает" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Совпадение с шаблоном" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Нет доступных лент." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Каналы не определены." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Нет элементов, соответствующих условиям поиска." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Правила не определены." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "РегЭкс" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Поисковый термин" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Поисковый термин" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Выбрать ленту" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Интервал" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Запуск при загрузке" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Правила загрузки" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Ленты" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Каналы торрентов" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Теги" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Тест соответствия шаблону" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Дней" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Часы" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Минут" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Назначение торрента" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Интервал должен быть положительным целым числом." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Неверное регулярное выражение." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Вы должны выбрать канал." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Вы должны указать место назначения." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Необходимо указать метку." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Вы должны указать корректный URL канала." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Пустой каталог." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood не имеет разрешения на чтение этого каталога." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Этот путь не существует. Он будет создан." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Произошла неизвестная ошибка. Пожалуйста, попробуйте еще раз." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Получение структуры директорий..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Родительская папка" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Все" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Активный" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Проверка" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Скачивание" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Ошибка" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Неактивный" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Раздача" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Остановлено" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Фильтр по статусу" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Фильтр по тегу" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Фильтровать по трекеру" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Без тегов" + } + ], + "general.ago": [ + { + "type": 0, + "value": "назад" + } + ], + "general.at": [ + { + "type": 0, + "value": "в" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Скопировано" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Копировать" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Произошла неизвестная ошибка" + } + ], + "general.of": [ + { + "type": 0, + "value": "из" + } + ], + "general.to": [ + { + "type": 0, + "value": "до" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Автоматически" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Произошла ошибка при запуске mediainfo на сервере. Убедитесь, что mediainfo установлен и доступен в PATH для Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Извлечение..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Вывод Mediainfo" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Очистить все" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Предмет в очереди" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Показано" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Сообщение об ошибке" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Загрузка завершена" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Не загружать" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Высокий" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Низкий" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Обычный" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "О Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Скачать слоты по всему" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Скачать слоты на торрент" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Доступность слота" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Глобальная загрузка слотов" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Загрузить слоты на торрент" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Шаблоны комбобоксов: Скачать" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Шаблоны комбобоксов: Загрузить" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Глобальная скорость загрузки" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Глобальный тротттл скорости загрузки" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Процент трансфертов" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Включить DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT порт" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Децентрализованное обнаружение пиров" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Входящие соединения" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Сообщил IP/имя хоста" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Максимум HTTP-соединений" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Включить обмен пирами" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Желаемые узлы" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Личеры" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Максимум пиров" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Минимум пиров" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Максимум раздачи пиров" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Минимум раздачи пиров" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Открыть порт" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Случайный порт" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Диапазон портов" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Точки монтирования диска" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Показать" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Проверить хэш при завершении" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Папка загрузок по умолчанию" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Диск" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Максимальное количество открытых файлов" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Память" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Максимальное использование памяти" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "О программе" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Проверка подлинности" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Трафик" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Подключение" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Использование диска" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Настройки" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Ресурсы" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Интерфейс пользователя" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Элементы контекстного меню" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Детали Торрента" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Язык" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Язык" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Показать" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Включено" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "В расширенном виде теги работают лучше в конце списка." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Отображение списка торрентов" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Размер торрента" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Сжатый вид" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Расширенный вид" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Ленты" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Выйти" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Настройки" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Ограничения скорости" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Поиск торрентов" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "СКАЧАТЬ" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "ЗАГРУЗИТЬ" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Загружено" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Загружено" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Неограниченный" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Бесплатно" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Использование диска" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Итого" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Использовано" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "из" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Добавить торрент" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Необязательные cookie-name=cookie-значение" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Печенье" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Назначение" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Назначение" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Добавить торренты" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Начать торрент" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Создать" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "или нажмите для просмотра" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Перетащите файлы сюда" + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "По файлу" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Торрент URL или Magnet ссылка" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "По URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Теги" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Торренты" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Необязательный базовый файл или имя каталога торрента" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Базовое имя" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Дополнительный комментарий в торрент-файле" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Комментарий" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Необязательная запись в infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Источник информации" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Приватный" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Источник" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Теги в потопе. Не добавлены в торрент." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "URL трекера" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Трекеры" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Использовать как базовый путь" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Выполнено" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Пауза" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Начать" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Остановить" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Детали" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Файлы" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Скачайте файл" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Загрузите файлы" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Загрузка деталей файла..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Комментарий" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " подключено к " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Добавлено" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Дата создания" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Загружено" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Свободное место на диске" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Хэш" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Общие положения" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Торрент" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Трекер" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Перевод" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Местоположение" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Нет" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Личеры" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Планировщик" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Игнорировать" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Пробежал" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Сиды" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Размер" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Теги" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Сообщение трекера" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Тип" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Приватный" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Публичный" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Личеры" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Нет данных для этого торрента." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " Выбранный файл" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " выбрал файлы" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Установить приоритет" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Трекеры" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Для этого торрента нет данных трекера." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Тип" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Не удается подключиться к клиенту." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Очистить фильтры" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Проверить хэш" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Детали торрента" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Скачать" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Установить местоположение торрента" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Пауза" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Приоритет" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Удалить" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Установить теги" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Установить трекеры" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Начать" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Остановить" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Перетащите файлы сюда, чтобы добавить их." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Нет торрентов для отображения." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Установить местоположение" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Настройка..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Проверять хэш" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Переместить данные" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Установить местоположение торрента" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Комментарий" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Дата создания" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Добавлено" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Скорость загрузки" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Загружено" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Свободное место на диске" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Хэш" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Игнорировать планировщик" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Приватный" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Наименование" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Личеры" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Процент завершённых" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Коэффициент" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Сиды" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Размер файла" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Теги" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Сообщение трекера" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Трекеры" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Скорость отдачи" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Загружено" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Удалить торренты" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Вы уверены, что хотите удалить " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " торрент" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " торренты" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "? ?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Удалить данные" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Вы не выбрали ни одного торрента." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Установить теги" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Введите теги" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Установить теги" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Установить трекеры" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Введите трекер" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Установить трекеры" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Загрузка трекеров..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Сортировать по" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "В" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "Гб" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "кБ" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "МБ" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "ТБ" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/с" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "д" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "час" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "м" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "с" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "ск" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/sr.json b/client/src/javascript/i18n/compiled/sr.json new file mode 100644 index 000000000..876af72fd --- /dev/null +++ b/client/src/javascript/i18n/compiled/sr.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Remove Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Successfully saved settings." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Successfully added " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Failed to add " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Successfully moved " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Failed to move " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Successfully removed " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Failed to remove " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Add User" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Create Account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Create an account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Welcome to Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Current User" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Username cannot be empty." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log In" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Login" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in to your account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "User is not Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Password" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "User Accounts" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Username" + } + ], + "button.add": [ + { + "type": 0, + "value": "Add" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancel" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "New" + } + ], + "button.no": [ + { + "type": 0, + "value": "No" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Save Settings" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Save" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Adding..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Yes" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Connection could not be verified." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Connectivity Issue" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent List" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Transfer History" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Add Download Rule" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Add Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Apply Tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Browse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclude" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exclude Pattern" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Existing Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Existing Rules" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Label" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Match" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matches" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match Pattern" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "No feeds available." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "No feeds defined." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No items matching search term." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "No rules defined." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Select Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start on load" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Download Rules" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Days" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Hours" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutes" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "The interval must be a positive integer." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Invalid regular expression." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "You must select a feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "You must specify a destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "You must specify a label." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "You must specify a valid feed URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Empty directory." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood does not have permission to read this directory." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "This path does not exist. It will be created." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Fetching directory structure..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Parent Directory" + } + ], + "filter.all": [ + { + "type": 0, + "value": "All" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Active" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Checking" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloading" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Error" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactive" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stopped" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filter by Status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filter by Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filter by Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Untagged" + } + ], + "general.ago": [ + { + "type": 0, + "value": "ago" + } + ], + "general.at": [ + { + "type": 0, + "value": "at" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copied" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copy" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "of" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatic" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Fetching..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Output" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Clear All" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Showing" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Error Reported" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Finished Downloading" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Don't Download" + } + ], + "priority.high": [ + { + "type": 0, + "value": "High" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Low" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "About Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download Slots Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slot Availability" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload Slots Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown Presets: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown Presets: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global Download Rate Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global Upload Rate Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Transfer Rate Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Enable DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralized Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Incoming Connections" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Reported IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximum HTTP Connections" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Enable Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Peers Desired" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximum Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimum Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximum Peers Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum Peers Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Open Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Randomize Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Range" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verify Hash on Completion" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Default Download Directory" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximum Open Files" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memory" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Max Memory Usage" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentication" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandwidth" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectivity" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Settings" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resources" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "User Interface" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Language" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Locale" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Enabled" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In the expanded view, tags work best at the end of the list." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent List Display" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Size" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Condensed View" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Expanded View" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log Out" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Settings" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Speed Limits" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Search torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Unlimited" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Free" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Used" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "of" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Add Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "or click to browse" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Drop some files here," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "By File" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL or Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "By URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Use as Base Path" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Details" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Files" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download File" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download Files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Loading file detail..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connected of " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "General" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfer" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "None" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Scheduler" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignored" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Size" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "There is no peer data for this torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected file" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Set Priority" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "There is no tracker data for this torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Clear Filters" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Check Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priority" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Remove" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "No torrents to display." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Set Location" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Setting..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Check hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Move data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Download Speed" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignore Scheduler" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Name" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percent Complete" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "File Size" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Upload Speed" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Remove Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Are you sure you want to remove " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Delete data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "You haven't selected any torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Enter tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sort By" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "hr" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/sv.json b/client/src/javascript/i18n/compiled/sv.json new file mode 100644 index 000000000..6dbb43271 --- /dev/null +++ b/client/src/javascript/i18n/compiled/sv.json @@ -0,0 +1,2616 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Lägg till Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Ta bort Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Starta Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stoppa Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Inställningarna har sparats." + } + ], + "alert.torrent.add": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Kunde inte lägga till " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Framgångsrikt flyttat " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Det gick inte att flytta " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Lyckades ta bort " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Det gick inte att ta bort " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Lägg till användare" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Administratör" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Skapa konto" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Skapa ett konto" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Välkommen till översvämningen!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Nuvarande användare" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Lösenordet kan inte vara tomt." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Användarnamnet får inte vara tomt." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Logga in" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Inloggning" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Logga in på ditt konto." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Användaren är inte Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Lösenord" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Användarkonton" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Användarnamn" + } + ], + "button.add": [ + { + "type": 0, + "value": "Lägg till" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Avbryt" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Hämta" + } + ], + "button.new": [ + { + "type": 0, + "value": "Ny" + } + ], + "button.no": [ + { + "type": 0, + "value": "Nej" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Spara inställningar" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Spara" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Lägger till..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Ja" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Kan inte ansluta till klienten" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Anslutningen kunde inte verifieras." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Klient" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Anslutningsinställningar kan inte vara tomt." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Lösenord" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Lösenord" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL till qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Användarnamn" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Användarnamn" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Värd" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Värdnamn eller IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Sökväg" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Sökväg till uttag" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Typ av anslutning" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Kan inte ansluta till klienten. Uppdatera anslutningsinställningarna." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Problem med anslutning" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Aviseringar" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent lista" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomi" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Historik över dataöverföring" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Detaljer för dataöverföringshastighet" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Lägg till nedladdningsregel" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Lägg till flöde" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Tillämplig flöde" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Tillämpa taggar" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Bläddra bland flöden" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exkludera" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exkludera mönster" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Befintliga flöden" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Befintliga regler" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Intervall" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Etikett" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Matcha" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matcha" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matcha" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Matcha mönster" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Inga flöden tillgängliga." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Inga flöden har definierats." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Inga objekt matchar söktermen." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Inga regler definierade." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Sök termin" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Sök termin" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Välj flöde" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Intervall" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Starta vid laddning" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Ladda ner regler" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Flöden" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Flöden för Torrent" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Taggar" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Testa matchande mönster" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Dagar" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Timmar" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minuter" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Intervallet måste vara ett positivt heltal." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Ogiltigt reguljärt uttryck." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Du måste välja ett flöde." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Du måste ange en destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Du måste ange en etikett." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Du måste ange en giltig flödes-URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Tom katalog." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Översvämningen har inte behörighet att läsa denna katalog." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Denna väg existerar inte. Den kommer att skapas." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Ett okänt fel inträffade. Försök igen." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Hämtar katalogstruktur..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Överordnad katalog" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Alla" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Aktiv" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Kontrollerar" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Hämtar" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Fel" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inaktiv" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stoppad" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filtrera efter status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filtrera efter tagg" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filtrera efter Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Otaggade" + } + ], + "general.ago": [ + { + "type": 0, + "value": "sedan" + } + ], + "general.at": [ + { + "type": 0, + "value": "vid" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Kopierad" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Kopiera" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Ett okänt fel uppstod" + } + ], + "general.of": [ + { + "type": 0, + "value": "av" + } + ], + "general.to": [ + { + "type": 0, + "value": "till" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatisk" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Ett fel uppstod när mediainfo kördes på servern. Kontrollera att mediainfo är installerat och tillgängligt i PATH till Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Hämtar..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo utdata" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Rensa alla" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed objekt köad" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Visar" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Fel rapporterat" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Hämtning slutförd" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Ladda inte ner" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Hög" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Låg" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Om översvämning" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Ladda ner Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Ladda ner Slots per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Tillgänglighet för Slot" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Ladda upp Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Ladda upp Slots Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown förinställningar: Ladda ner" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown förinställningar: Ladda upp" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global nedladdningshastighet Trottel" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global uppladdningshastighet Trottel" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Överföring hastighet trottoarer" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Aktivera DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentraliserad Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Inkommande anslutningar" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Rapporterat IP/värdnamn" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximal HTTP-anslutning" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Aktivera Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Jämlikar önskade" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Klienter" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximalt antal klienter" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minsta antal klienter" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximal klientfröning" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minsta antal klienter Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Öppna port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Slumpa port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Portens intervall" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk användning monteringspunkter" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Visa" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verifiera Hash vid slutförd" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Förvald nedladdningsmapp" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Diskett" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximalt antal öppna filer" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Minne" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Maximal minnesanvändning" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Om" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Autentisering" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandbredd" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Anslutning" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Diskanvändning" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Inställningar" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resurser" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Användargränssnitt" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Kontext menyobjekt" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detaljkolumner" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Språk" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Lokalt" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Visa" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Aktiverad" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "I den utvidgade vyn fungerar taggar bäst i slutet av listan." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Visning av torrentlista" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Storlek" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Kondenserad vy" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Utökad vy" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Flöden" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Logga ut" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Inställningar" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Hastighetsgränser" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Sök torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "LADDA NER" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "LADDA UPP" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Nedladdad" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uppladdad" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Obegränsad" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Gratis" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Diskanvändning" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Totalt" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Använt" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "av" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Lägg till Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Valfritt cookie-namn=cookie-värde" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Mål" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Mål" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Lägg till Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Starta Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Skapa" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "eller klicka för att bläddra" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Släpp några filer här," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "Efter fil" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL eller Magnet länk" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "Efter URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Taggar" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Valfri basfil eller katalognamn för torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Grundnamn" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Valfri kommentar i torrent-fil" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Valfri källpost i infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Källa" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Källa" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Taggar i översvämning. Ej tillagd till skapad torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Spårare URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Spårare" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Använd som grundsökväg" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Slutförd" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pausa" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Starta" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stoppa" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Detaljer" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Filer" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Ladda ner fil" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Ladda ner filer" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Laddar fildetalj..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " ansluten till " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Tillagd" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Skapad datum" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Nedladdad" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Ledigt diskutrymme" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Allmänt" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Spårare" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Överföring" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Plats" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Ingen" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Klienter" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Schemaläggare" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignorerad" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Lydd" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Frön" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Storlek" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Taggar" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Spårare Meddelande" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Typ" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Publik" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Klienter" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Det finns inga peer-data för denna torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " vald fil" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " valda filer" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Ange prioritet" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Spårare" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Det finns ingen spårningsdata för denna torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Typ" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Kan inte ansluta till klienten." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Rensa filter" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Kontrollera hasch" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Detaljer" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Hämta" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Ange torrentplats" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pausa" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Prioritet" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Radera" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Ange taggar" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Ange spårare" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Starta" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stoppa" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Släpp filer här för att lägga till dem." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Inga torrents att visa." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Ange plats" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Inställer..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Kontrollera hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Flytta data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Ange torrentplats" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Kommentar" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Skapad datum" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Tillagd" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Ladda ner hastighet" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Nedladdad" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Ledigt diskutrymme" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignorera schemaläggare" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Privat" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Namn" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Klienter" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Procent slutförd" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Förhållande" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Frön" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Filstorlek" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Taggar" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Spårare Meddelande" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Spårare" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Ladda upp hastighet" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uppladdad" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Ta bort torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Är du säker på att du vill ta bort " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Ta bort data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Du har inte valt några torrenter." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Ange taggar" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Ange taggar" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Ange taggar" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Ange spårare" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Ange en tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Ange spårare" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Laddar spårare..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sortera efter" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "Tuberkulos" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "tim" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "vk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/tr.json b/client/src/javascript/i18n/compiled/tr.json new file mode 100644 index 000000000..876af72fd --- /dev/null +++ b/client/src/javascript/i18n/compiled/tr.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Remove Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Successfully saved settings." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Successfully added " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Failed to add " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Successfully moved " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Failed to move " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Successfully removed " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Failed to remove " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Add User" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Create Account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Create an account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Welcome to Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Current User" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Username cannot be empty." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log In" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Login" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in to your account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "User is not Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Password" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "User Accounts" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Username" + } + ], + "button.add": [ + { + "type": 0, + "value": "Add" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancel" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "New" + } + ], + "button.no": [ + { + "type": 0, + "value": "No" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Save Settings" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Save" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Adding..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Yes" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Connection could not be verified." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Connectivity Issue" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent List" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Transfer History" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Add Download Rule" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Add Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Apply Tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Browse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclude" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exclude Pattern" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Existing Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Existing Rules" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Label" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Match" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matches" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match Pattern" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "No feeds available." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "No feeds defined." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No items matching search term." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "No rules defined." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Select Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start on load" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Download Rules" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Days" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Hours" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutes" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "The interval must be a positive integer." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Invalid regular expression." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "You must select a feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "You must specify a destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "You must specify a label." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "You must specify a valid feed URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Empty directory." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood does not have permission to read this directory." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "This path does not exist. It will be created." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Fetching directory structure..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Parent Directory" + } + ], + "filter.all": [ + { + "type": 0, + "value": "All" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Active" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Checking" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloading" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Error" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactive" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stopped" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filter by Status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filter by Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filter by Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Untagged" + } + ], + "general.ago": [ + { + "type": 0, + "value": "ago" + } + ], + "general.at": [ + { + "type": 0, + "value": "at" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copied" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copy" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "of" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatic" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Fetching..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Output" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Clear All" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Showing" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Error Reported" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Finished Downloading" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Don't Download" + } + ], + "priority.high": [ + { + "type": 0, + "value": "High" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Low" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "About Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download Slots Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slot Availability" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload Slots Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown Presets: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown Presets: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global Download Rate Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global Upload Rate Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Transfer Rate Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Enable DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralized Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Incoming Connections" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Reported IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximum HTTP Connections" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Enable Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Peers Desired" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximum Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimum Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximum Peers Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum Peers Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Open Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Randomize Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Range" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verify Hash on Completion" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Default Download Directory" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximum Open Files" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memory" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Max Memory Usage" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentication" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandwidth" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectivity" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Settings" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resources" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "User Interface" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Language" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Locale" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Enabled" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In the expanded view, tags work best at the end of the list." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent List Display" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Size" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Condensed View" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Expanded View" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log Out" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Settings" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Speed Limits" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Search torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Unlimited" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Free" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Used" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "of" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Add Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "or click to browse" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Drop some files here," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "By File" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL or Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "By URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Use as Base Path" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Details" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Files" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download File" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download Files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Loading file detail..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connected of " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "General" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfer" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "None" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Scheduler" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignored" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Size" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "There is no peer data for this torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected file" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Set Priority" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "There is no tracker data for this torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Clear Filters" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Check Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priority" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Remove" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "No torrents to display." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Set Location" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Setting..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Check hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Move data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Download Speed" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignore Scheduler" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Name" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percent Complete" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "File Size" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Upload Speed" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Remove Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Are you sure you want to remove " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Delete data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "You haven't selected any torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Enter tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sort By" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "hr" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/uk.json b/client/src/javascript/i18n/compiled/uk.json new file mode 100644 index 000000000..d52198997 --- /dev/null +++ b/client/src/javascript/i18n/compiled/uk.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Додати Торрент" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Прибрати торент" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Почати торент" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Зупинити торрент" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Налаштування успішно збережені." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Успішно додано " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Не вдалося додати " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "торент" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Успішно переміщено " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "торент" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Не вдалося перемістити " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "торент" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Успішно видалено " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "торент" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Не вдалося видалити " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "торент" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "торрент" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Добавити користувача" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Адмін" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Створити обл. запис" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Створити акаунт" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Ласкаво просимо до Флуту!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Поточний користувач" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Пароль не може бути порожнім." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Ім'я користувача не може бути порожнім." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Увійти" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Логін" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Увійти до свого облікового запису." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "Користувач не є адміністратором" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Пароль" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "Облікові записи користувачів" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Ім'я користувача" + } + ], + "button.add": [ + { + "type": 0, + "value": "Додати" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Скасувати" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Звантажити" + } + ], + "button.new": [ + { + "type": 0, + "value": "Новий" + } + ], + "button.no": [ + { + "type": 0, + "value": "Ні" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Зберегти Налаштування" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Зберегти" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Додаємо..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Так" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Не вдається підключитися до клієнта" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Неможливо перевірити підключення." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Клієнт" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Параметри підключення не можуть бути порожніми." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "кБіторрент" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Пароль" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Пароль" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "Адреса" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL на qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Ім'я користувача" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Ім'я користувача" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Хост" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Ім'я хоста або IP адреса" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Порт" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Порт" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Шлях" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Шлях до сокету" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Тип з'єднання" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Не вдається підключитися до клієнта. Будь ласка, оновіть параметри підключення." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Проблема з підключенням" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Сповіщення" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Торент-список" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Торент таксономії" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Історія передавання даних" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Деталі частоти передачі даних" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Додати правило завантаження" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Добавити RSS Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Стрічка додатків" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Застосувати мітки" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Перегляд стрічок" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Не включати" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Виключити ключ" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Існуючі подачі" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Існуючі правила" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Проміжок" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Мітка" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Матч" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " співпадають" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " матчів" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Шаблон матчу" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "Немає доступних каналів." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "Каналів не визначено." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "Немає елементів відповідних пошуковому запиту." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "Правила не визначені." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "Реджекс" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Шукати за словом" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Шукати за словом" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Вибрати стрічку" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Проміжок" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Запустити при завантаженні" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Правила завантаження" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Стрічки новин" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Торент фіди" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Мітки" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Тест шаблону матчу" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Днів" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Годин" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Хвилин" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Призначення торенту" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "Адреса" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "Інтервал має бути додатним цілим числом." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Недійсний регулярний вираз." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "Ви повинні вибрати канал." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "Ви повинні вказати місце призначення." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "Будь ласка, вкажіть мітку." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "Необхідно вказати дійсний URL каналу." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Тека порожня." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Флуд не має дозволу на читання цієї теки." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "Цей шлях не існує. Він буде створений." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "Сталася невідома помилка, спробуйте ще раз." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Отримання структури каталогу..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Батьківський каталог" + } + ], + "filter.all": [ + { + "type": 0, + "value": "Всі" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Активний" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Перевірка" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Завантажується" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Помилка" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Неактивний" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Сідую" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Зупинено" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Фільтр за станом" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Фільтр за ярликами" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Фільтр по трекеру" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Без тегів" + } + ], + "general.ago": [ + { + "type": 0, + "value": "тому" + } + ], + "general.at": [ + { + "type": 0, + "value": "під" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Скопійовано" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Копія" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "Сталася невідома помилка" + } + ], + "general.of": [ + { + "type": 0, + "value": "з" + } + ], + "general.to": [ + { + "type": 0, + "value": "по" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Автоматично" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "Виникла помилка під час роботи mediainfo на сервері. Перевірте, що mediainfo встановлений і доступний у PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Отримання..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Вивід Середньовіччя" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Очистити все" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Черга RSS Feed'у" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Показано" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Повідомлення про помилку" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Завершене завантаження" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Не завантажувати" + } + ], + "priority.high": [ + { + "type": 0, + "value": "Високий" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Низька" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Нормальний" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "Про Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Завантажити слоти глобальним" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Завантажити слоти на торент" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Наявність слоту" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Глобальна передача слотів" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Відвантажити слоти за торент" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Пресети випадаючого списку: Завантажити" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Пресети випадаючого списку: Завантаження" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Збільшення швидкості завантаження" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Збільшення швидкості вивантаження" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Збільшення швидкості передачі" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Увімкнути DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT порт" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Децентралізований канал для учасника" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Вхідні підключення" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Повідомлено IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Максимальна HTTP підключень" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Увімкнути обмін учасниками" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Учасників Бажано" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Учасники" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Максимальна кількість учасників" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Мінімум учасників" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Максимальна кількість учасників роздачі" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Мінімум учасників на роздачі" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Відкритий порт" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Перемішати порт" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Діапазон портів" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Очки для використання диску" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Показати" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Перевірити хеш при завершенні" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Тека для завантажень за замовчуванням" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Диск" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Максимум відкритих файлів" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Пам'ять" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Макс. використання пам'яті" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "Про програму" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Автентифікація" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Ширина пропускної здатності" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Підключення" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Використання диску" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Налаштування" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Ресурси" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "Інтерфейс користувача" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Контекстне меню" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Стовпці подробиць торенту" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Мова:" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Локалізація" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Показати" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Увімкнено" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "У розширеному перегляді теги в кінці списку працюють найкраще." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Відображення торрентів" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Розмір торенту" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Стиснутий вигляд" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Розширений вигляд" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Стрічки новин" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Вийти з системи" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Налаштування" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Обмеження швидкості" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Пошук торентів" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "ЗАВАНТАЖИТИ" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "ВИВАНТАЖИТИ" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Завантажено" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Відвантажено" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Необмежено" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Безкоштовно" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Використання диску" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Загальна сума замовлення" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Використано" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "з" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Додати Торрент" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Необов'язкове значення cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Файли Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Пункт призначення" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Пункт призначення" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Додати торенти" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Почати торент" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Створити" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "або клацніть для перегляду" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Перетягніть сюди файли," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "За файлом" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Адреса торенту або Magnet-посилання" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "За URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Мітки" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Торенти" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Необов'язковий файл або назва теки торента" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Базова назва" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Необов'язковий коментар у торент-файлі" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Коментар" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Необов'язковий вихідний запис в infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Інформація про джерело" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Приватний" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Джерело" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Теги в потоці. Не додано до створеного торенту." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "URL трекера" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Трекери" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Використовувати як Базовий шлях" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Завершені" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Пауза" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Старт" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Зупинити" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Подробиці" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Файли" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Завантажити файл" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Завантажити файл" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Завантаження подробиць файлу..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Коментар" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " підключено до " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Додано" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Дата створення" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Завантажено" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Вільний дисковий простір" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Хеш" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "Загальні налаштування" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Торент" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Трекер" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Переказ" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Місцезнаходження" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "Без ефекту" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Учасники" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Планувальник" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ігнорується" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Підборіддя" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Насіння" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Розмір" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Мітки" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Трекер повідомлення" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Тип" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Приватний" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Загальнодоступна" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Учасники" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "Немає даних для учасників цього торенту." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " Обраний файл" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " обрані файли" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Встановити пріоритет" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Трекери" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "Немає даних трекеру для цього торенту." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Тип" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Неможливо підключитися до клієнта." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Скинути фільтри" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Перевірити хеш" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Деталі торенту" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Звантажити" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Встановити розташування торентів" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Пауза" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Пріоритет" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Видалити" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Встановити позначки" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Встановити трекери" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Старт" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Зупинити" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Перетягніть файли сюди, щоб додати їх." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "Немає торентів для відображення." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Задати місцезнаходження" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Налаштування..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Перевірити хеш" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Переміщення даних" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Встановити розташування торентів" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Коментар" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Дата створення" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Додано" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Швидкість завантаження" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Завантажено" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Вільний дисковий простір" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Хеш" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ігнорувати планувальник" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Приватний" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Ім'я" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Учасники" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Процент завершено" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Співвідношення" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Насіння" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "Розмір файлу" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Мітки" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Трекер повідомлення" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Трекери" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Швидкість вивантаження" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Відвантажено" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Вилучити торенти" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Ви впевнені, що хочете видалити " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " торент" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " торрентів" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Видалити дані" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "Ви не вибрали жодного торенту." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Встановити позначки" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Введіть мітки" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Встановити позначки" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Встановити трекери" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Ввести трекер" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Встановити трекери" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Завантаження трекерів..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Сортувати за" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "Сі" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "ГБ" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "кБ" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "МБ" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "ТБ" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/с" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "д" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "год" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "м" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "с" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "тижд" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/vi.json b/client/src/javascript/i18n/compiled/vi.json new file mode 100644 index 000000000..876af72fd --- /dev/null +++ b/client/src/javascript/i18n/compiled/vi.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Remove Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Successfully saved settings." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Successfully added " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Failed to add " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Successfully moved " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Failed to move " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Successfully removed " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Failed to remove " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Add User" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Create Account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Create an account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Welcome to Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Current User" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Username cannot be empty." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log In" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Login" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in to your account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "User is not Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Password" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "User Accounts" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Username" + } + ], + "button.add": [ + { + "type": 0, + "value": "Add" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancel" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "New" + } + ], + "button.no": [ + { + "type": 0, + "value": "No" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Save Settings" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Save" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Adding..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Yes" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Connection could not be verified." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Connectivity Issue" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent List" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Transfer History" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Add Download Rule" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Add Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Apply Tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Browse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclude" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exclude Pattern" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Existing Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Existing Rules" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Label" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Match" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matches" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match Pattern" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "No feeds available." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "No feeds defined." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No items matching search term." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "No rules defined." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Select Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start on load" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Download Rules" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Days" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Hours" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutes" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "The interval must be a positive integer." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Invalid regular expression." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "You must select a feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "You must specify a destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "You must specify a label." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "You must specify a valid feed URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Empty directory." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood does not have permission to read this directory." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "This path does not exist. It will be created." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Fetching directory structure..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Parent Directory" + } + ], + "filter.all": [ + { + "type": 0, + "value": "All" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Active" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Checking" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloading" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Error" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactive" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stopped" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filter by Status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filter by Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filter by Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Untagged" + } + ], + "general.ago": [ + { + "type": 0, + "value": "ago" + } + ], + "general.at": [ + { + "type": 0, + "value": "at" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copied" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copy" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "of" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatic" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Fetching..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Output" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Clear All" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Showing" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Error Reported" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Finished Downloading" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Don't Download" + } + ], + "priority.high": [ + { + "type": 0, + "value": "High" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Low" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "About Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download Slots Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slot Availability" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload Slots Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown Presets: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown Presets: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global Download Rate Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global Upload Rate Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Transfer Rate Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Enable DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralized Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Incoming Connections" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Reported IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximum HTTP Connections" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Enable Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Peers Desired" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximum Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimum Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximum Peers Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum Peers Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Open Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Randomize Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Range" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verify Hash on Completion" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Default Download Directory" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximum Open Files" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memory" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Max Memory Usage" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentication" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandwidth" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectivity" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Settings" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resources" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "User Interface" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Language" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Locale" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Enabled" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In the expanded view, tags work best at the end of the list." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent List Display" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Size" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Condensed View" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Expanded View" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log Out" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Settings" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Speed Limits" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Search torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Unlimited" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Free" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Used" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "of" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Add Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "or click to browse" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Drop some files here," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "By File" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL or Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "By URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Use as Base Path" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Details" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Files" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download File" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download Files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Loading file detail..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connected of " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "General" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfer" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "None" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Scheduler" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignored" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Size" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "There is no peer data for this torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected file" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Set Priority" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "There is no tracker data for this torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Clear Filters" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Check Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priority" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Remove" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "No torrents to display." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Set Location" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Setting..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Check hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Move data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Download Speed" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignore Scheduler" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Name" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percent Complete" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "File Size" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Upload Speed" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Remove Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Are you sure you want to remove " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Delete data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "You haven't selected any torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Enter tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sort By" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "hr" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/zh-Hans.json b/client/src/javascript/i18n/compiled/zh-Hans.json new file mode 100644 index 000000000..fb4c6af76 --- /dev/null +++ b/client/src/javascript/i18n/compiled/zh-Hans.json @@ -0,0 +1,2344 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "添加种子" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "移除种子" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "启动种子" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "停止种子" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "设置保存成功!" + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "成功添加 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 个种子。" + } + ], + "alert.torrent.add.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 个种子添加失败。" + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "移动 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 个种子成功。" + } + ], + "alert.torrent.move.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 个种子移动失败。" + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "成功移除 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 个种子。" + } + ], + "alert.torrent.remove.failed": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 个种子移除失败。" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "增加用户" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "管理员" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "创建账号" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "创建一个账号" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "欢迎使用 Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "当前用户" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "密码不能为空。" + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "用户名不能为空" + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "登录" + } + ], + "auth.login": [ + { + "type": 0, + "value": "登录" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "登录您的帐户。" + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "用户不是管理员" + } + ], + "auth.password": [ + { + "type": 0, + "value": "密码" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "用户账号" + } + ], + "auth.username": [ + { + "type": 0, + "value": "用户名" + } + ], + "button.add": [ + { + "type": 0, + "value": "新增" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "取消" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "下载" + } + ], + "button.new": [ + { + "type": 0, + "value": "添加" + } + ], + "button.no": [ + { + "type": 0, + "value": "取消" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "保存设置" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "保存" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "正在添加..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "是" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "无法连接到客户端" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "无法建立连接。" + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "客户端:" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "连接设置不能为空。" + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBitTorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "密码" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "密码" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "网址" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "qBittorrent Web API 的 URL" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "用户名" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "用户名" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "主机" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "主机名或IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "端口" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "端口" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "路径" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "套接字路径" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "连接类型" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "无法连接到客户端。请更新连接设置。" + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "连接失败" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "通知" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "种子列表" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "种子分类" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "数据传输历史" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "数据传输速率详细信息" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "增加下载规则" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "添加订阅源" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "可用的订阅源" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "变更标签" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "浏览订阅源" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "排除" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "排除规则" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "现有的订阅源" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "现有的规则" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "间隔" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "标签" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "匹配" + } + ], + "feeds.match.count": [ + { + "type": 0, + "value": "# 个匹配" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "匹配规则" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "没有可用的订阅源。" + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "没有定义订阅源。" + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "没有匹配搜索词的项目。" + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "没有定义规则。" + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "正则" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "搜索关键词" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "搜索词" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "选择订阅源" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "间隔" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "读取后开始" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "下载规则" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "订阅源" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "种子源" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "标签" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "测试匹配模式" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "天" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "时" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "分" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "下载位置" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "网址" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "间隔必须是一个正整数。" + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "无效的RegEx。" + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "你必须选择一个订阅源。" + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "你必须指定下载位置。" + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "你必须指定标签。" + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "你必须指定有效的订阅源网址。" + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "空目录。" + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood没有权限读取此目录。" + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "路径不存在,将会创建此目录。" + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "发生未知错误。请重试。" + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "获取目录结构..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "父目录" + } + ], + "filter.all": [ + { + "type": 0, + "value": "全部" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "活动中" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "校验中" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "已完成" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "下载中" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "错误" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "非活动中" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "做种中" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "已停止" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "按状态筛选" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "按标签筛选" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "按Tracker筛选" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "无标签" + } + ], + "general.ago": [ + { + "type": 0, + "value": "前" + } + ], + "general.at": [ + { + "type": 0, + "value": "在" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "复制成功" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "复制" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "发生未知错误" + } + ], + "general.of": [ + { + "type": 0, + "value": "的" + } + ], + "general.to": [ + { + "type": 0, + "value": "到" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "自动" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "在服务器上运行mediainfo时发生错误。检查mediainfo是否已经安装,并且在PATH中!" + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "获取中..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo 输出" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "全部清除" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "新闻源排队了" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "显示" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "错误报告" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "下载完成!" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "不下载" + } + ], + "priority.high": [ + { + "type": 0, + "value": "高" + } + ], + "priority.low": [ + { + "type": 0, + "value": "低" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "正常" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "关于Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "全局下载槽数" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "每个种子下载槽数" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "传输槽位限制" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "全局上传槽数" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "每个种子上传槽数" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "下载速度限制菜单" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "上传速度限制菜单" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "全局下载速度限制" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "全局上传速度限制" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "传输速度限制" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "开启 DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT 端口" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "分散式节点发现" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "传入连接" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "上报 IP/主机名" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "最大HTTP连接数" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "启用节点交换" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "期望节点数" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "节点" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "最大节点数" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "最小节点数" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "最大做种数" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "最小做种数" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "开放端口" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "随机端口" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "端口范围" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "磁盘挂载点" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "显示" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "完成后校验" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "默认下载目录" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "磁盘" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "最大打开文件数" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "内存" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "最大内存使用" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "关于" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "认证" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "带宽" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "连接" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "磁盘使用" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "设置" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "资源" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "用户接口" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "上下文菜单项" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "种子详情列表" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "语言" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "本地化" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "显示" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "启用" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "在展开模式中,标签最好能够在列表末尾。" + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "种子列表显示模式" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "种子大小" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "紧凑模式" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "展开模式" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "订阅源" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "登出" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "设置" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "速度限制" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "搜索种子" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "下载" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "上传" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "已下载" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "已上传" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "无限制" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "免费的" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "磁盘使用量" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "总计" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "已使用" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " / " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "/" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "添加种子" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "可选的 cookie-name=cookie值" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookie" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "下载位置" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "下载位置" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "添加种子" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "开始下载" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "创建" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "或点击浏览" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "将文件拖至此处," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "上传文件" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "种子网址或磁力链接" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "从网址" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "标签" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "种子" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "种子的可选基础文件或目录名称" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "基本名称" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "种子文件中的可选评论" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "评论" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "信息哈希中的可选源项" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "信息来源" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "非公开的" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "来源" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Flood中的标签。没有添加到创建种子。" + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "追踪器 URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "追踪器" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "作为基础路径" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "已完成" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "暂停" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "开始" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "停止" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "详情" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "文件" + } + ], + "torrents.details.files.download.file": [ + { + "type": 0, + "value": "下载文件" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "正在加载详细信息..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "评论" + } + ], + "torrents.details.general.connected": [ + { + "type": 0, + "value": "已连接到 " + }, + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " / " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "添加时间" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "创建时间" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "已下载" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "可用磁盘空间" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "哈希" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "概况" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "种子" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "追踪器" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "传输" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "位置" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "无" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "节点" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "调度器" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "已忽略" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "已遵守" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "做种者" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "大小" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "标签" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker 信息" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "类型" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "私有" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "公开" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "节点" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "这个种子没有节点信息。" + } + ], + "torrents.details.selected.files": [ + { + "type": 0, + "value": "已选择 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 个文件" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "设置优先级" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Tracker 服务器" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "这个种子没有 Tracker 信息。" + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "类型" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "无法连接到客户端。" + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "清除筛选" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "校验" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "种子详情" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "下载" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "设置种子位置" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "暂停" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "优先级" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "移除" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "设置标签" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "设置追踪器" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "开始" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "停止" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "将文件拖放到此处以添加它们。" + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "没有可显示的种子。" + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "设置路径" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "设置中..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "校验" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "移动数据" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "设置种子路径" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "备注" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "创建时间" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "添加时间" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "下载速度" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "已下载" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "剩余时间" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "可用磁盘空间" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "哈希" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "忽略调度器" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "私有" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "名称" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "对等点" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "完成百分比" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "分享率" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "种子" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "文件大小" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "标签" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker 信息" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "追踪器" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "上传速度" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "已上传" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "移除种子" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "你确定你想要删除 # 个种子" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "删除数据" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "您没有选择任何种子。" + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "设置标签" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "输入标签" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "设置标签" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "设置追踪器" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "输入追踪器" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "设置追踪器" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "正在加载追踪器..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "排序方式" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "千B" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/秒" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "天" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "时" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "分" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "秒" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "周" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "年" + } + ] +} diff --git a/client/src/javascript/i18n/compiled/zh-Hant.json b/client/src/javascript/i18n/compiled/zh-Hant.json new file mode 100644 index 000000000..bb8439133 --- /dev/null +++ b/client/src/javascript/i18n/compiled/zh-Hant.json @@ -0,0 +1,2382 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "新增種子" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "移除種子" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "開始下載" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "停止下載" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "設定儲存成功。" + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "成功新增 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 個種子。" + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "新增 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 個種子失敗。" + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "成功移動 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 個種子。" + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "移動 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 個種子失敗。" + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "成功移除 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 個種子。" + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "移除 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 個種子失敗。" + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "新增使用者" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "管理員" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "建立帳號" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "建立一個帳號" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "歡迎使用 Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "目前使用者" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "密碼不能留空。" + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "使用者名稱不可以留空!" + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "清除" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "登入" + } + ], + "auth.login": [ + { + "type": 0, + "value": "登入" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "登入你的帳號" + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "使用者不是管理員" + } + ], + "auth.password": [ + { + "type": 0, + "value": "密碼" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "使用者帳號" + } + ], + "auth.username": [ + { + "type": 0, + "value": "使用者名稱" + } + ], + "button.add": [ + { + "type": 0, + "value": "新增" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "取消" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "下載" + } + ], + "button.new": [ + { + "type": 0, + "value": "新增" + } + ], + "button.no": [ + { + "type": 0, + "value": "否" + } + ], + "button.ok": [ + { + "type": 0, + "value": "確定" + } + ], + "button.retry": [ + { + "type": 0, + "value": "重試" + } + ], + "button.save": [ + { + "type": 0, + "value": "儲存設定" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "儲存" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "新增中..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "是" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "更新連線設定" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "用目前的連線設定重試" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "如果這個錯誤持續發生,請聯繫Flood的管理員。" + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "無法建立連線。" + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "用戶端" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "連線設定不可以是空白的。" + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "密碼" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "密碼" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "網址" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "qBittorrent Web API 網址" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "使用者名稱" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "使用者名稱" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "主機" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "主機名稱或IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "連接埠" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "連接埠" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "路徑" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Socket路徑" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "連線類型" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "透過TCP曝露rTorrent可能會會有特權提升問題。" + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "密碼" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "密碼" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "網址" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "Transmission RPC 介面網址" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "使用者名稱" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "使用者名稱" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "無法連線到用戶端,請更新您的連線設定。" + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "連線問題" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "通知" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "種子列表" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "種子分類" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "資料傳輸紀錄" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "詳細資料傳輸率" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "新增下載規則" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "新增訊息來源" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "可使用的訊息來源" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "套用標籤" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "瀏覽訊息來源" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "此為規則驗證,不會保存或送出。" + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "除外" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "例外Pattern" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "已存在的訊息來源" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "已存在的規則 " + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "內部" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "標籤" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "符合" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " 項符合" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " 項符合" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "符合Pattern" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "沒有可用的訊息來源" + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "沒有任何訊息來源" + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "沒有符合搜尋條件的項目" + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "沒有任何規則。" + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "正則表達式" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "搜尋關鍵字" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "搜尋條件" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "選擇訊息來源" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "內部" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "讀取後開始" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "下載規則" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "訊息來源(Feeds)" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "種子訊息來源(Feeds)" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "標籤" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "測試Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "日" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "小時" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "分鐘" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "下載位置" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "網址" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "警告限制必須是正整數" + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "無效的正則表達式。" + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "你必須選擇一個訊息來源。" + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "您必須指定下載位置。" + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "你必須指定標籤。" + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "你必須指定一個有效的訊息來源網址。" + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "空白資料夾。" + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood 沒有權限去讀取資料夾的內容。" + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "路徑不存在,系統會自動建立。" + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "發生未知錯誤,請再試一次。" + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "抓取資料夾結構中..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "上一層目錄" + } + ], + "filter.all": [ + { + "type": 0, + "value": "全部" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "活躍的" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "檢查中" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "已完成" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "下載中" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "錯誤" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "不活躍的" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "做種中" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "已停止" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "狀態篩選" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "標籤篩選" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Tracker篩選" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "未標籤" + } + ], + "general.ago": [ + { + "type": 0, + "value": "前" + } + ], + "general.at": [ + { + "type": 0, + "value": "於" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "已複製" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "複製" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "發生未知的錯誤" + } + ], + "general.of": [ + { + "type": 0, + "value": "的" + } + ], + "general.to": [ + { + "type": 0, + "value": "給" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "自動" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "執行 MediaInfo 發生錯誤,請確認系統中有安裝 mediainfo 且該執行檔位於 PATH 變數中。" + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "抓取中..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo輸出" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "全部清除" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "已排入佇列" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "檢視" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "錯誤回報" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "下載完成" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "不要下載" + } + ], + "priority.high": [ + { + "type": 0, + "value": "高優先權" + } + ], + "priority.low": [ + { + "type": 0, + "value": "低優先權" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "中優先權" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "關於Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "最大同時下載數" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "每個 Torrent 最大下載數" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "傳輸個數限制" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "最大同時上傳數" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "每個 Torrent 最大上傳數" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "下拉選單可選擇下載速度" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "下拉選單可選擇上傳速度" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "總下載速度限制" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "總上傳速度限制" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "傳輸速度限制" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "啟用 DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT 連接埠" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "分散式節點發現" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "連入連線" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "回報IP/主機名稱" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "最大 HTTP 連線" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "啟用節點交換" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "需要節點數" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "節點" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "最大節點數" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "最小節點數" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "最大作種節點數" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "最小作種節點數" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "開放連接埠" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "隨機連接埠" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "連接埠範圍" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "掛載節點磁碟空間使用量" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "顯示" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "完成時驗證雜湊" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "預設下載資料夾" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "磁碟" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "最大開啟檔案數" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "記憶體" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "最大記憶體使用量" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "關於" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "驗證" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "頻寬" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "連線" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "磁碟使用量" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "設定" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "資源" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "使用者介面" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "右鍵選單項目" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "種子詳細資訊欄位" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "語言" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "語系" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "複選" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "單選" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "顯示" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "啟用" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "在展開模式中,把標籤放在列表的最後一項會比較好。" + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "種子列表顯示模式" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "種子大小" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "壓縮模式" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "展開模式" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "訊息來源" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "登出" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "設定" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "速度限制" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "搜尋種子" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "下載速度" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "上傳速度" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "已下載" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "已上傳" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "無限制" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "可用" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "磁碟空間使用量" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "總計" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "已使用" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " / " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "中的" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "新增種子" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "(選填) cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "下載位置" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "下載位置" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "新增種子" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "新增後開始下載" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "建立" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "或點擊瀏覽" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "拖曳檔案到這個視窗" + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "從檔案" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "種子網址或磁力連結" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "從網址" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "標籤" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "種子" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "名稱" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "來源" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker 網址" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "當作基本路徑" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "暫停" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "開始" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "停止" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "詳細資訊" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "檔案" + } + ], + "torrents.details.files.download.file": [ + { + "type": 0, + "value": "下載檔案" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "讀取檔案詳細資訊..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "評論" + } + ], + "torrents.details.general.connected": [ + { + "type": 0, + "value": "已連線到 " + }, + { + "type": 1, + "value": "total" + }, + { + "type": 0, + "value": " 中的 " + }, + { + "type": 1, + "value": "connected" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "新增日期" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "建立日期" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "已下載" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "可用磁碟空間" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "雜湊值" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "通用" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "種子" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "傳輸" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "位置" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "無" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "節點" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "排程" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "忽略" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "直接下載" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "節點使用者" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "大小" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "標籤" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker訊息" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "類型" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "私有" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "公開" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "MediaInfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "節點" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "這個種子沒有任何節點資訊。" + } + ], + "torrents.details.selected.files": [ + { + "type": 0, + "value": "已選擇 " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " 個檔案" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "設定優先權" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "這個種子沒有任何 tracker 資訊。" + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "類型" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "清除篩選條件" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "檢查雜湊" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "種子詳細資訊" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "下載" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "設定種子位置" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "暫停" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "優先權" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "移除" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "設定標籤" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "開始" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "停止" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "沒有任何種子可以顯示。" + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "設定下載位置" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "設定中..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "檢查雜湊" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "移動資料" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "設定下載位置" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "評論" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "建立日期" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "新增日期" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "下載速度" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "已下載" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "剩餘時間" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "可用磁碟空間" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "雜湊" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "忽略排程" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "私有" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "任務名稱" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "節點" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "完成比例" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "分享率" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "種子" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "檔案大小" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "標籤" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker 訊息" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "上傳速度" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "已上傳" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "移除種子" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "你確定要移除這些種子嗎?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "刪除已下載檔案" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "你沒有選擇任何種子。" + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "設定標籤" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "輸入標籤" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "設定標籤" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "排序方式" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "KB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/秒" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "天" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "時" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "分" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "秒" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "週" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "年" + } + ] +} diff --git a/client/src/javascript/i18n/de.js b/client/src/javascript/i18n/de.js deleted file mode 100644 index d020edb05..000000000 --- a/client/src/javascript/i18n/de.js +++ /dev/null @@ -1,5 +0,0 @@ -export default { - 'sidebar.button.settings': 'Einstellungen', - - 'button.save.feed': 'Speichern', -}; diff --git a/client/src/javascript/i18n/en.js b/client/src/javascript/i18n/en.js deleted file mode 100644 index 9c22070ab..000000000 --- a/client/src/javascript/i18n/en.js +++ /dev/null @@ -1,355 +0,0 @@ -export default { - 'actionbar.button.start.torrent': 'Start Torrent', - 'actionbar.button.stop.torrent': 'Stop Torrent', - 'actionbar.button.add.torrent': 'Add Torrent', - 'actionbar.button.remove.torrent': 'Remove Torrent', - - 'alert.torrent.add': `Successfully added {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.add.failed': `Failed to add {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.move': `Successfully moved {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.move.failed': `Failed to move {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.remove': `Successfully removed {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.torrent.remove.failed': `Failed to remove {countElement} {count, plural, - =1 {torrent} - other {torrents} - }.`, - 'alert.settings.saved': 'Successfully saved settings.', - - 'auth.add.user': 'Add User', - 'auth.connectionType': 'rTorrent Connection Type', - 'auth.connectionType.tcp': 'TCP', - 'auth.connectionType.socket': 'Unix Socket', - 'auth.create.account': 'Create Account', - 'auth.create.an.account': 'Create an account', - 'auth.create.an.account.intro': 'Welcome to Flood!', - 'auth.current.user': 'Current User', - 'auth.error.username.empty': 'Username cannot be empty.', - 'auth.log.in': 'Log In', - 'auth.login': 'Login', - 'auth.password': 'Password', - 'auth.user.accounts': 'User Accounts', - 'auth.username': 'Username', - 'auth.admin': 'Admin', - 'auth.message.not.admin': 'User is not Admin', - 'auth.rtorrentHost': 'rTorrent Host', - 'auth.rtorrentPort': 'rTorrent Port', - 'auth.rtorrentSocket': 'rTorrent Socket', - 'auth.rtorrentSocketPath': 'rTorrent Socket Path', - - 'button.add': 'Add', - 'button.cancel': 'Cancel', - 'button.no': 'No', - 'button.save': 'Save Settings', - 'button.save.feed': 'Save', - 'button.test': 'Test', - 'button.state.adding': 'Adding...', - 'button.yes': 'Yes', - - 'connectivity.modal.title': 'Connectivity Issue', - 'connectivity.modal.content': 'Cannot connect to rTorrent. Please update the information now.', - - 'feeds.add.automatic.download.rule': 'Add Download Rule', - 'feeds.add.feed': 'Add Feed', - 'feeds.applicable.feed': 'Applicable Feed', - 'feeds.apply.tags': 'Apply Tags', - 'feeds.exclude.pattern': 'Exclude Pattern', - 'feeds.existing.feeds': 'Existing Feeds', - 'feeds.existing.rules': 'Existing Rules', - 'feeds.label': 'Label', - 'feeds.match.count': '{count, plural, =1 {# match} other {# matches}}', - 'feeds.match.pattern': 'Match Pattern', - 'feeds.match': 'Match', - 'feeds.exclude': 'Exclude', - 'feeds.no.feeds.available': 'No feeds available.', - 'feeds.no.feeds.defined': 'No feeds defined.', - 'feeds.no.rules.defined': 'No rules defined.', - 'feeds.regEx': 'RegEx', - 'feeds.select.feed': 'Select Feed', - 'feeds.select.interval': 'Interval', - 'feeds.start.on.load': 'Start on load', - 'feeds.tabs.download.rules': 'Download Rules', - 'feeds.tabs.feeds': 'Feeds', - 'feeds.tabs.heading': 'Torrent Feeds', - 'feeds.tags': 'Tags', - 'feeds.time.hr': 'Hours', - 'feeds.time.min': 'Minutes', - 'feeds.time.day': 'Days', - 'feeds.torrent.destination': 'Torrent Destination', - 'feeds.url': 'URL', - 'feeds.search': 'Search term', - 'feeds.validation.invalid.regular.expression': 'Invalid regular expression.', - 'feeds.validation.must.select.feed': 'You must select a feed.', - 'feeds.validation.must.specify.destination': 'You must specify a destination.', - 'feeds.validation.must.specify.label': 'You must specify a label.', - 'feeds.validation.must.specify.valid.feed.url': 'You must specify a valid feed URL.', - - 'filesystem.empty.directory': 'Empty directory.', - 'filesystem.error.eacces': 'Flood does not have permission to read this directory.', - 'filesystem.error.enoent': 'This path does not exist. It will be created.', - 'filesystem.fetching': 'Fetching directory structure...', - - 'filter.all': 'All', - 'filter.status.title': 'Filter by Status', - 'filter.status.downloading': 'Downloading', - 'filter.status.completed': 'Complete', - 'filter.status.active': 'Active', - 'filter.status.inactive': 'Inactive', - 'filter.status.error': 'Error', - 'filter.tracker.title': 'Filter by Tracker', - 'filter.tag.title': 'Filter by Tag', - 'filter.untagged': 'Untagged', - - 'general.ago': 'ago', - 'general.at': 'at', - 'general.to': 'to', - 'general.of': 'of', - - 'general.clipboard.copy': 'Copy', - 'general.clipboard.copied': 'Copied', - - 'locale.language.en': 'English', - 'locale.language.fr': 'French', - 'locale.language.ko': 'Korean', - 'locale.language.nl': 'Nederlands', - 'locale.language.zh': 'Chinese', - - 'mediainfo.execError': - 'An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.', - 'mediainfo.fetching': 'Fetching...', - 'mediainfo.heading': 'Mediainfo Output', - - 'notification.torrent.finished.heading': 'Finished Downloading', - 'notification.torrent.finished.body': '{name}', - 'notification.torrent.errored.heading': 'Error Reported', - 'notification.torrent.errored.body': '{name}', - 'notification.clear.all': 'Clear All', - 'notification.showing': 'Showing', - - 'priority.dont.download': "Don't Download", - 'priority.high': 'High', - 'priority.low': 'Low', - 'priority.normal': 'Normal', - - 'settings.bandwidth.slots.download.divider.label': 'Download Slots Divider', - 'settings.bandwidth.slots.download.global.label': 'Download Slots Global', - 'settings.bandwidth.slots.download.label': 'Download Slots Per Torrent', - 'settings.bandwidth.slots.heading': 'Slot Availability', - 'settings.bandwidth.slots.upload.divider.label': 'Upload Slots Divider', - 'settings.bandwidth.slots.upload.global.label': 'Upload Slots Global', - 'settings.bandwidth.slots.upload.label': 'Upload Slots Per Torrent', - 'settings.bandwidth.transferrate.dropdown.preset.download.label': 'Dropdown Presets: Download', - 'settings.bandwidth.transferrate.dropdown.preset.upload.label': 'Dropdown Presets: Upload', - 'settings.bandwidth.transferrate.global.throttle.download': 'Global Download Rate Throttle', - 'settings.bandwidth.transferrate.global.throttle.upload': 'Global Upload Rate Throttle', - 'settings.bandwidth.transferrate.heading': 'Transfer Rate Throttles', - - 'settings.connectivity.dht.label': 'Enable DHT', - 'settings.connectivity.dht.port.label': 'DHT Port', - 'settings.connectivity.dpd.heading': 'Decentralized Peer Discovery', - 'settings.connectivity.incoming.heading': 'Incoming Connections', - 'settings.connectivity.ip.hostname.label': 'Reported IP/Hostname', - 'settings.connectivity.max.http.connections': 'Maximum HTTP Connections', - 'settings.connectivity.peer.exchange.label': 'Enable Peer Exchange', - 'settings.connectivity.peers.desired.label': 'Peers Desired', - 'settings.connectivity.peers.heading': 'Peers', - 'settings.connectivity.peers.max.label': 'Maximum Peers', - 'settings.connectivity.peers.min.label': 'Minimum Peers', - 'settings.connectivity.peers.seeding.max.label': 'Maximum Peers Seeding', - 'settings.connectivity.peers.seeding.min.label': 'Minimum Peers Seeding', - 'settings.connectivity.port.open.label': 'Open Port', - 'settings.connectivity.port.randomize.label': 'Randomize Port', - 'settings.connectivity.port.range.label': 'Port Range', - - 'settings.resources.disk.check.hash.label': 'Verify Hash on Completion', - 'settings.resources.disk.download.location.label': 'Default Download Directory', - 'settings.resources.disk.heading': 'Disk', - 'settings.resources.max.open.files': 'Maximum Open Files', - 'settings.resources.memory.heading': 'Memory', - 'settings.resources.memory.max.label': 'Max Memory Usage', - - 'settings.tabs.bandwidth': 'Bandwidth', - 'settings.tabs.connectivity': 'Connectivity', - 'settings.tabs.heading': 'Settings', - 'settings.tabs.resources': 'Resources', - 'settings.tabs.authentication': 'Authentication', - 'settings.tabs.userinterface': 'User Interface', - 'settings.tabs.diskusage': 'Disk Usage', - 'settings.tabs.about': 'About', - - 'settings.ui.locale': 'Locale', - 'settings.ui.language': 'Language', - 'settings.ui.torrent.list': 'Torrent List Display', - 'settings.ui.torrent.size': 'Torrent Size', - 'settings.ui.torrent.size.expanded': 'Expanded View', - 'settings.ui.torrent.size.condensed': 'Condensed View', - 'settings.ui.torrent.details.enabled': 'Enabled', - 'settings.ui.torrent.details.tags.placement': 'In the expanded view, tags work best at the end of the list.', - - 'settings.diskuseage.show': 'Show', - 'settings.diskuseage.mount.points': 'Disk Usage Mount Points', - - 'settings.about.flood': 'About Flood', - - 'sidebar.button.feeds': 'Feeds', - 'sidebar.button.notifications': 'Notifications', - 'sidebar.button.settings': 'Settings', - 'sidebar.button.speedlimits': 'Speed Limits', - 'sidebar.button.log.out': 'Log Out', - - 'sidebar.search.placeholder': 'Search torrents', - - 'sidebar.transferdata.downloaded': 'Downloaded', - 'sidebar.transferdata.uploaded': 'Uploaded', - - 'speed.unlimited': 'Unlimited', - - 'unit.size.byte': 'B', - 'unit.size.kilobyte': 'kB', - 'unit.size.megabyte': 'MB', - 'unit.size.gigabyte': 'GB', - 'unit.size.terabyte': 'TB', - 'unit.speed': '{baseUnit}/s', - 'unit.time.year': 'yr', - 'unit.time.week': 'wk', - 'unit.time.day': 'd', - 'unit.time.hour': 'hr', - 'unit.time.minute': 'm', - 'unit.time.second': 's', - 'unit.time.infinity': '∞', - - 'torrents.add.button.add': 'Add Torrent', - 'torrents.add.destination.label': 'Destination', - 'torrents.add.destination.placeholder': 'Destination', - 'torrents.add.heading': 'Add Torrents', - 'torrents.add.start.label': 'Start Torrent', - 'torrents.add.tab.file.browse': 'or click to browse', - 'torrents.add.tab.file.drop': 'Drop some files here,', - 'torrents.add.tab.file.title': 'By File', - 'torrents.add.tab.url.input.placeholder': 'Torrent URL or Magnet Link', - 'torrents.add.tab.url.title': 'By URL', - 'torrents.add.torrents.label': 'Torrents', - - 'torrents.destination.base_path': 'Use as Base Path', - - 'torrents.details.actions.pause': 'Pause', - 'torrents.details.actions.start': 'Start', - 'torrents.details.actions.stop': 'Stop', - 'torrents.details.details': 'Details', - 'torrents.details.files': 'Files', - 'torrents.details.files.loading': 'Loading file detail...', - 'torrents.details.files.download.file': `{count, plural, - =1 {Download File} - other {Download Files} - }`, - 'torrents.details.general.comment': 'Comment', - 'torrents.details.general.connected': '{connected} connected of {total}', - 'torrents.details.general.date.added': 'Added', - 'torrents.details.general.date.created': 'Creation Date', - 'torrents.details.general.downloaded': 'Downloaded', - 'torrents.details.general.free.disk.space': 'Free Disk Space', - 'torrents.details.general.hash': 'Hash', - 'torrents.details.general.heading.general': 'General', - 'torrents.details.general.heading.torrent': 'Torrent', - 'torrents.details.general.heading.tracker': 'Tracker', - 'torrents.details.general.heading.transfer': 'Transfer', - 'torrents.details.general.location': 'Location', - 'torrents.details.general.none': 'None', - 'torrents.details.general.peers': 'Peers', - 'torrents.details.general.scheduler.ignored': 'Ignored', - 'torrents.details.general.scheduler.obeyed': 'Obeyed', - 'torrents.details.general.scheduler': 'Scheduler', - 'torrents.details.general.seeds': 'Seeds', - 'torrents.details.general.size': 'Size', - 'torrents.details.general.tags': 'Tags', - 'torrents.details.general.tracker.message': 'Tracker Message', - 'torrents.details.general.type.private': 'Private', - 'torrents.details.general.type.public': 'Public', - 'torrents.details.general.type': 'Type', - 'torrents.details.mediainfo': 'Mediainfo', - 'torrents.details.peers.no.data': 'There is no peer data for this torrent.', - 'torrents.details.peers': 'Peers', - 'torrents.details.selected.files': `{count, plural, - =1 {{countElement} selected file} - other {{countElement} selected files} - }`, - 'torrents.details.selected.files.set.priority': 'Set Priority', - 'torrents.details.trackers.no.data': 'There is no tracker data for this torrent.', - 'torrents.details.trackers.type': 'Type', - 'torrents.details.trackers': 'Trackers', - - 'torrents.list.clear.filters': 'Clear Filters', - 'torrents.list.context.check.hash': 'Check Hash', - 'torrents.list.context.details': 'Torrent Details', - 'torrents.list.context.move': 'Set Torrent Location', - 'torrents.list.context.pause': 'Pause', - 'torrents.list.context.priority': 'Priority', - 'torrents.list.context.remove': 'Remove', - 'torrents.list.context.set.tags': 'Set Tags', - 'torrents.list.context.start': 'Start', - 'torrents.list.context.stop': 'Stop', - 'torrents.list.no.torrents': 'No torrents to display.', - 'torrents.list.drop': 'Drop files here to add them to rTorrent.', - 'torrents.list.cannot.connect': 'Cannot connect to rTorrent.', - 'torrent.list.peers': '{connected} {of} {total}', - 'torrent.list.peers.of': 'of', - - 'torrents.move.button.set.location': 'Set Location', - 'torrents.move.button.state.setting': 'Setting...', - 'torrents.move.data.label': 'Move data', - 'torrents.move.heading': 'Set Torrent Location', - - 'torrents.properties.date.added': 'Added', - 'torrents.properties.base.path': 'Base Path', - 'torrents.properties.comment': 'Comment', - 'torrents.properties.creation.date': 'Creation Date', - 'torrents.properties.download.speed': 'Download Speed', - 'torrents.properties.download.total': 'Downloaded', - 'torrents.properties.eta': 'ETA', - 'torrents.properties.free.disk.space': 'Free Disk Space', - 'torrents.properties.hash': 'Hash', - 'torrents.properties.ignore.schedule': 'Ignore Scheduler', - 'torrents.properties.is.private': 'Private', - 'torrents.properties.name': 'Name', - 'torrents.properties.percentage': 'Percent Complete', - 'torrents.properties.ratio': 'Ratio', - 'torrents.properties.size': 'File Size', - 'torrents.properties.tags': 'Tags', - 'torrents.properties.tracker.message': 'Tracker Message', - 'torrents.properties.upload.speed': 'Upload Speed', - 'torrents.properties.upload.total': 'Uploaded', - - 'torrents.remove.are.you.sure': `Are you sure you want to remove {count, plural, - =0 {no torrents} - =1 {one torrent} - other {# torrents} - }?`, - 'torrents.remove.delete.data': 'Delete data', - 'torrents.remove.error.no.torrents.selected': "You haven't selected any torrents.", - 'torrents.remove': 'Remove Torrents', - - 'torrents.set.tags.button.set': 'Set Tags', - 'torrents.set.tags.heading': 'Set Tags', - 'torrents.set.tags.enter.tags': 'Enter tags', - - 'torrents.sort.title': 'Sort By', - - 'connection-interruption.heading': 'Cannot connect to rTorrent', - 'connection-interruption.verify-settings-prompt': "Let's verify your connection settings.", - 'connection-interruption.verification-error': 'Connection could not be verified.', - 'connection-interruption.verification-success': 'Connection successful', -}; diff --git a/client/src/javascript/i18n/es.js b/client/src/javascript/i18n/es.js deleted file mode 100644 index 8ad34491b..000000000 --- a/client/src/javascript/i18n/es.js +++ /dev/null @@ -1,317 +0,0 @@ -export default { - 'alert.torrent.add': `{countElement} {count, plural, - =1 {torrent agregado} - other {torrents agregados} - }.`, - 'alert.torrent.add.failed': `{countElement} {count, plural, - =1 {torrent fallo al agregar} - other {torrents fallaron al agregarse} - }.`, - 'alert.torrent.move': `{countElement} {count, plural, - =1 {torrent se ha movido} - other {torrents se han movido} - }.`, - 'alert.torrent.move.failed': `{countElement} {count, plural, - =1 {torrent fallo al moverse} - other {torrents fallaron al moverse} - }.`, - 'alert.torrent.remove': `{countElement} {count, plural, - =1 {torrent se ha eliminado} - other {torrents se han eliminado} - }.`, - 'alert.torrent.remove.failed': `{countElement} {count, plural, - =1 {torrent fallo al eliminarse} - other {torrents fallaron al eliminarse} - }.`, - 'alert.settings.saved': 'Configuración Guardada.', - - 'auth.add.user': 'Agregar usuario', - 'auth.create.account': 'Crear Cuenta', - 'auth.create.an.account': 'Crear una Cuenta', - 'auth.error.username.empty': 'Nombre de usuario no puede estar en blanco.', - 'auth.log.in': 'Iniciar Sesión', - 'auth.login': 'Acceder', - 'auth.password': 'Contraseña', - 'auth.user.accounts': 'Cuentas de Usuario', - 'auth.username': 'Nombre de Usuario', - 'auth.admin': 'Admin', - 'auth.message.not.admin': 'El Usuario no es Administrador', - - 'button.add': 'Agregar', - 'button.cancel': 'Cancelar', - 'button.no': 'No', - 'button.save.feed': 'Guardar', - 'button.save': 'Guardar Configuración', - 'button.state.adding': 'Agregando...', - 'button.yes': 'Sí', - - 'feeds.add.automatic.download.rule': 'Agregar Regla de Descarga', - 'feeds.add.feed': 'Agregar Fuente', - 'feeds.applicable.feed': 'Fuente Correspondiente', - 'feeds.apply.tags': 'Aplicar Etiquetas', - 'feeds.exclude.pattern': 'Patrón de Exclusión', - 'feeds.existing.feeds': 'Fuentes Existentes', - 'feeds.existing.rules': 'Reglas Existentes', - 'feeds.label': 'Rótulo', - 'feeds.match.count': '{count, plural, =1 {# Encontrado} other {# Encontrados}}', - 'feeds.match.pattern': 'Patrón de Inclusión', - 'feeds.match': 'Encontrado', - 'feeds.no.feeds.available': 'No hay fuentes disponibles', - 'feeds.no.feeds.defined': 'No hay fuentes definidas', - 'feeds.no.rules.defined': 'No hay reglas definidas', - 'feeds.regEx': 'RegEx', - 'feeds.select.feed': 'Seleccionar Fuente', - 'feeds.select.interval': 'Intervalo', - 'feeds.start.on.load': 'Iniciar al Cargar', - 'feeds.tabs.download.rules': 'Reglas de Descarga', - 'feeds.tabs.feeds': 'Fuentes', - 'feeds.tabs.heading': 'Fuentes de Torrent', - 'feeds.tags': 'Etiquetas', - 'feeds.time.hr': '{durationValue} hr', - 'feeds.time.min': '{durationValue} min', - 'feeds.torrent.destination': 'Destinación de Torrent', - 'feeds.url': 'URL', - 'feeds.validation.invalid.regular.expression': 'RegEx invalida.', - 'feeds.validation.must.select.feed': 'Debe seleccionar una fuente', - 'feeds.validation.must.specify.destination': 'Debe seleccionar una destinación.', - 'feeds.validation.must.specify.label': 'Debe seleccionar un rotulo', - 'feeds.validation.must.specify.valid.feed.url': 'Debe seleccionar un URL de fuente valido.', - - 'filesystem.empty.directory': 'Carpeta vacía', - 'filesystem.error.eacces': 'Flood no tiene permisos para leer esta carpeta.', - 'filesystem.error.enoent': 'Esta ruta no existe. Se creara.', - 'filesystem.fetching': 'Cargando estructura de carpeta...', - - 'filter.all': 'Todo', - 'filter.status.title': 'Filtrar por Estado', - 'filter.status.downloading': 'Descargando', - 'filter.status.completed': 'Completo', - 'filter.status.active': 'Activo', - 'filter.status.inactive': 'Inactivo', - 'filter.status.error': 'Error', - 'filter.tracker.title': 'Filtrar por Tracker', - 'filter.tag.title': 'Filtrar por Etiqueta', - 'filter.untagged': 'Sin Etiqueta', - - 'general.ago': 'hace', - 'general.at': 'en', - 'general.to': 'hasta', - 'general.of': 'de', - - 'general.clipboard.copy': 'Copiar', - 'general.clipboard.copied': 'Copiado', - - 'locale.language.en': 'Inglés', - 'locale.language.es': 'Español', - 'locale.language.fr': 'Francés', - 'locale.language.ko': 'Coreano', - 'locale.language.nl': 'Holandés', - - 'mediainfo.execError': - 'Se ha encontrado un error al correr Mediainfo. Confirme que Mediainfo este instalado y disponible a Flood en el PATH', - 'mediainfo.fetching': 'Obteniendo...', - 'mediainfo.heading': 'Mediainfo', - - 'notification.torrent.finished.heading': 'Descarga Completada', - 'notification.torrent.finished.body': '{name}', - 'notification.torrent.errored.heading': 'Error Reportado', - 'notification.torrent.errored.body': '{name}', - 'notification.clear.all': 'Borrar Todos', - 'notification.showing': 'Mostrando', - - 'priority.dont.download': 'No Descargar', - 'priority.high': 'Alta', - 'priority.low': 'Baja', - 'priority.normal': 'Normal', - - 'settings.bandwidth.slots.download.divider.label': 'Divisor de Espacios de Descarga', - 'settings.bandwidth.slots.download.global.label': 'Cant. Global de Espacios de Descargas', - 'settings.bandwidth.slots.download.label': 'Cant. por Torrent de Espacios de Descarga', - 'settings.bandwidth.slots.heading': 'Disponibilidad de Espacios', - 'settings.bandwidth.slots.upload.divider.label': 'Divisor de Espacios de Subida', - 'settings.bandwidth.slots.upload.global.label': 'Cant. Global de Espacios de Subida', - 'settings.bandwidth.slots.upload.label': 'Cant. por Torrent de Espacios de Descarga', - 'settings.bandwidth.transferrate.dropdown.preset.download.label': 'Predeterminados de Desplegable: Descarga', - 'settings.bandwidth.transferrate.dropdown.preset.upload.label': 'Predeterminados de Desplegable: Subida', - 'settings.bandwidth.transferrate.global.throttle.download': 'Regulador Global de Velocidad de Descarga', - 'settings.bandwidth.transferrate.global.throttle.upload': 'Regulador Global de Velocidad de Subida', - 'settings.bandwidth.transferrate.heading': 'Reguladores de Velocidad', - - 'settings.connectivity.dht.label': 'Habilitar DHT', - 'settings.connectivity.dht.port.label': 'Puerto para DHT', - 'settings.connectivity.dpd.heading': 'Descubrimiento de Peers Descentralizado', - 'settings.connectivity.incoming.heading': 'Conexiones Entrantes', - 'settings.connectivity.ip.hostname.label': 'IP/Hostname Presentado', - 'settings.connectivity.max.http.connections': 'Máxima Cant. de Conexiones HTTP', - 'settings.connectivity.peer.exchange.label': 'Habilitar Intercambio de Peers', - 'settings.connectivity.peers.desired.label': 'Cant. de Peers Deseada', - 'settings.connectivity.peers.heading': 'Peers', - 'settings.connectivity.peers.max.label': 'Cant. Máxima de Peers', - 'settings.connectivity.peers.min.label': 'Cant. Mínima de Peers', - 'settings.connectivity.peers.seeding.max.label': 'Cant. Máxima de Peers en Seeding', - 'settings.connectivity.peers.seeding.min.label': 'Cant. Mínima de Peers en Seeding', - 'settings.connectivity.port.open.label': 'Puerto Abierto', - 'settings.connectivity.port.randomize.label': 'Puerto Aleatorio', - 'settings.connectivity.port.range.label': 'Rango de Puertos', - - 'settings.resources.disk.check.hash.label': 'Verificar Hash al Terminar', - 'settings.resources.disk.download.location.label': 'Carpeta de Descargas Predeterminada', - 'settings.resources.disk.heading': 'Disco', - 'settings.resources.max.open.files': 'Cant. Máxima de Archivos Abiertos', - 'settings.resources.memory.heading': 'Memoria', - 'settings.resources.memory.max.label': 'Utilización Máxima de Memoria', - - 'settings.tabs.bandwidth': 'Ancho de Banda', - 'settings.tabs.connectivity': 'Conectividad', - 'settings.tabs.heading': 'Configuración', - 'settings.tabs.resources': 'Recursos', - 'settings.tabs.authentication': 'Autenticación', - 'settings.tabs.userinterface': 'Interfaz de Usuario', - - 'settings.ui.locale': 'Región', - 'settings.ui.language': 'Idioma', - 'settings.ui.torrent.list': 'Exhibición de Torrents', - 'settings.ui.torrent.size': 'Tamaño de Torrent', - 'settings.ui.torrent.size.expanded': 'Modo Expandido', - 'settings.ui.torrent.size.condensed': 'Modo Condensado', - 'settings.ui.torrent.details.tags.placement': - 'En el modo expandido, las etiquetas se ven mejor al final de la lista.', - - 'sidebar.button.feeds': 'Fuentes', - 'sidebar.button.notifications': 'Notificaciones', - 'sidebar.button.settings': 'Ajustes', - 'sidebar.button.speedlimits': 'Limites de Velocidad', - - 'sidebar.search.placeholder': 'Buscar torrents', - - 'sidebar.transferdata.downloaded': 'Descargado', - 'sidebar.transferdata.uploaded': 'Subido', - - 'speed.unlimited': 'Sín Límite', - - 'unit.size.byte': 'B', - 'unit.size.kilobyte': 'kB', - 'unit.size.megabyte': 'MB', - 'unit.size.gigabyte': 'GB', - 'unit.size.terabyte': 'TB', - 'unit.speed': '{baseUnit}/s', - 'unit.time.year': 'yr', - 'unit.time.week': 'wk', - 'unit.time.day': 'd', - 'unit.time.hour': 'hr', - 'unit.time.minute': 'm', - 'unit.time.second': 's', - 'unit.time.infinity': '∞', - - 'torrents.add.button.add': 'Agregar Torrent', - 'torrents.add.destination.label': 'Destinación', - 'torrents.add.destination.placeholder': 'Destinación', - 'torrents.add.heading': 'Agregar Torrents', - 'torrents.add.start.label': 'Iniciar Descarga', - 'torrents.add.tab.file.browse': 'o haga clic para navegar', - 'torrents.add.tab.file.drop': 'Arrastrar archivos hacia aquí,', - 'torrents.add.tab.file.title': 'Con Archivo', - 'torrents.add.tab.url.input.placeholder': 'URL de Torrent o Magnet Link', - 'torrents.add.tab.url.title': 'Con URL', - 'torrents.add.torrents.label': 'Torrents', - - 'torrents.destination.base_path': 'Usar como Base Path', - - 'torrents.details.actions.pause': 'Pausar', - 'torrents.details.actions.start': 'Comenzar', - 'torrents.details.actions.stop': 'Parar', - 'torrents.details.details': 'Detalles', - 'torrents.details.files': 'Archivos', - 'torrents.details.files.download.file': `{count, plural, - =1 {Archivo} - other {Archivos} - }`, - 'torrents.details.general.comment': 'Comentario', - 'torrents.details.general.connected': '{connected} conectados de {total}', - 'torrents.details.general.date.added': 'Agregado en', - 'torrents.details.general.date.created': 'Creado en', - 'torrents.details.general.downloaded': 'Descargado', - 'torrents.details.general.free.disk.space': 'Espacio de Disco Libre', - 'torrents.details.general.hash': 'Hash', - 'torrents.details.general.heading.general': 'General', - 'torrents.details.general.heading.torrent': 'Torrent', - 'torrents.details.general.heading.tracker': 'Tracker', - 'torrents.details.general.heading.transfer': 'Transferencia', - 'torrents.details.general.location': 'Carpeta', - 'torrents.details.general.none': 'Ninguno', - 'torrents.details.general.peers': 'Peers', - 'torrents.details.general.scheduler.ignored': 'Ignorado', - 'torrents.details.general.scheduler.obeyed': 'Seguido', - 'torrents.details.general.scheduler': 'Programador', - 'torrents.details.general.seeds': 'Seeds', - 'torrents.details.general.size': 'Tamaño', - 'torrents.details.general.tags': 'Etiquetas', - 'torrents.details.general.tracker.message': 'Mensaje de Tracker', - 'torrents.details.general.type.private': 'Privado', - 'torrents.details.general.type.public': 'Publico', - 'torrents.details.general.type': 'Tipo', - 'torrents.details.mediainfo': 'Mediainfo', - 'torrents.details.peers.no.data': 'No existen Peers para este torrent.', - 'torrents.details.peers': 'Peers', - 'torrents.details.selected.files': `{count, plural, - =1 {{countElement} archivo seleccionado} - other {{countElement} archivos seleccionados} - }`, - 'torrents.details.selected.files.set.priority': 'Ajustar Prioridad', - 'torrents.details.trackers.no.data': 'No existe información de tracker para este torrent.', - 'torrents.details.trackers.type': 'Tipo', - 'torrents.details.trackers': 'Trackers', - - 'torrents.list.clear.filters': 'Borrar Filtros', - 'torrents.list.context.check.hash': 'Verificar Hash', - 'torrents.list.context.details': 'Detalles de Torrent', - 'torrents.list.context.move': 'Configurar Carpeta de Descarga', - 'torrents.list.context.pause': 'Pausar', - 'torrents.list.context.priority': 'Prioridad', - 'torrents.list.context.remove': 'Eliminar', - 'torrents.list.context.set.tags': 'Configurar Etiquetas', - 'torrents.list.context.start': 'Comenzar', - 'torrents.list.context.stop': 'Parar', - 'torrents.list.no.torrents': 'No hay torrents', - 'torrent.list.peers': '{connected} {of} {total}', - 'torrent.list.peers.of': 'de', - - 'torrents.move.button.set.location': 'Configurar', - 'torrents.move.button.state.setting': 'Guardando...', - 'torrents.move.data.label': 'Mover archivos', - 'torrents.move.heading': 'Configurar Carpeta de Descarga', - - 'torrents.properties.date.added': 'Agregado en', - 'torrents.properties.base.path': 'Carpeta Base', - 'torrents.properties.comment': 'Comentario', - 'torrents.properties.creation.date': 'Creado en', - 'torrents.properties.download.speed': 'Velocidad de Descarga', - 'torrents.properties.download.total': 'Cant. Descargada', - 'torrents.properties.eta': 'ETA', - 'torrents.properties.free.disk.space': 'Espcio de Disco Libre', - 'torrents.properties.hash': 'Hash', - 'torrents.properties.ignore.schedule': 'Ignorar Programador', - 'torrents.properties.is.private': 'Privado', - 'torrents.properties.name': 'Nombre', - 'torrents.properties.percentage': 'Porcentaje Descargado', - 'torrents.properties.ratio': 'Ratio', - 'torrents.properties.size': 'Tamaño de Archivo', - 'torrents.properties.tags': 'Etiquetas', - 'torrents.properties.tracker.message': 'Mensaje de Tracker', - 'torrents.properties.upload.speed': 'Velocidad de Subida', - 'torrents.properties.upload.total': 'Cant. Subida', - - 'torrents.remove.are.you.sure': `¿Seguro que quiere eliminar {count, plural, - =0 {cero torrents} - =1 {un torrent} - other {# torrents} - }?`, - 'torrents.remove.delete.data': 'Eliminar datos', - 'torrents.remove.error.no.torrents.selected': 'No ha seleccionado un torrent.', - 'torrents.remove': 'Eliminar Torrents', - - 'torrents.set.tags.button.set': 'Configurar', - 'torrents.set.tags.heading': 'Configurar Etiquetas', - - 'torrents.sort.title': 'Ordenar Por', -}; diff --git a/client/src/javascript/i18n/fr.js b/client/src/javascript/i18n/fr.js deleted file mode 100644 index ffce6e2f4..000000000 --- a/client/src/javascript/i18n/fr.js +++ /dev/null @@ -1,281 +0,0 @@ -export default { - 'auth.add.user': 'Ajouter un Utilisateur', - 'auth.create.account': 'Créer un Compte', - 'auth.create.an.account': 'Créer un Compte', - 'auth.error.username.empty': "Le nom d'utilisateur ne peut pas être vide.", - 'auth.log.in': 'Connexion', - 'auth.login': 'Identifiant', - 'auth.password': 'Mot de Passe', - 'auth.user.accounts': 'Comptes', - 'auth.username': "Nom d'Utilisateur", - 'auth.admin': 'Admin', - 'auth.message.not.admin': "L'Utilisateur n'est pas Administrateur", - - 'button.add': 'Ajouter', - 'button.cancel': 'Annuler', - 'button.no': 'Non', - 'button.save.feed': 'Enregistrer', - 'button.save': 'Enregistrer', - 'button.state.adding': 'Ajout...', - 'button.yes': 'Oui', - - 'feeds.add.automatic.download.rule': 'Ajouter une règle de téléchargement', - 'feeds.add.feed': 'Ajouter un Flux', - 'feeds.applicable.feed': 'Flux Applicable', - 'feeds.apply.tags': 'Appliquer les Tags', - 'feeds.exclude.pattern': 'Exclure le Motif', - 'feeds.existing.feeds': 'Flux Existants', - 'feeds.existing.rules': 'Règles Existantes', - 'feeds.label': 'Label', - 'feeds.match.count': '{count, plural, =1 {# correspond} other {# correspondent}}', - 'feeds.match.pattern': 'Motif Correspondant', - 'feeds.match': 'Correspondance', - 'feeds.no.feeds.available': 'Aucun flux disponible.', - 'feeds.no.feeds.defined': 'Aucun flux défini.', - 'feeds.no.rules.defined': 'Aucune règle définie.', - 'feeds.regEx': 'RegEx', - 'feeds.select.feed': 'Sélectionner un Flux', - 'feeds.select.interval': 'Intervalle', - 'feeds.start.on.load': 'Démarrer Immédiatement', - 'feeds.tabs.download.rules': 'Règles de Téléchargement', - 'feeds.tabs.feeds': 'Flux', - 'feeds.tabs.heading': 'Flux de Torrent', - 'feeds.tags': 'Tags', - 'feeds.time.hr': '{durationValue} h', - 'feeds.time.min': '{durationValue} min', - 'feeds.torrent.destination': 'Destination du Torrent', - 'feeds.url': 'URL', - 'feeds.validation.invalid.regular.expression': 'Expression régulière invalide.', - 'feeds.validation.must.select.feed': 'Vous devez choisir un flux.', - 'feeds.validation.must.specify.destination': 'Vous devez définir un emplacement.', - 'feeds.validation.must.specify.label': 'Vous devez définir un label.', - 'feeds.validation.must.specify.valid.feed.url': 'Vous devez définir une URL de flux valide.', - - 'filter.all': 'Tout', - 'filter.status.title': 'Filtrer par Status', - 'filter.status.downloading': 'En Téléchargement', - 'filter.status.completed': 'Terminé', - 'filter.status.active': 'Actif', - 'filter.status.inactive': 'Inactif', - 'filter.status.error': 'Erreur', - 'filter.tracker.title': 'Filtrer par Traqueur', - 'filter.tag.title': 'Filtrer par Tag', - 'filter.untagged': 'Sans Tag', - - 'locale.language.en': 'Anglais', - 'locale.language.fr': 'Français', - 'locale.language.ko': 'Coréen', - 'locale.language.nl': 'Néerlandais', - - 'alert.torrent.add': `L'ajout de {countElement} {count, plural, - =1 {torrent} - other {torrents} - } a réussi.`, - 'alert.torrent.add.failed': `L'ajout de {countElement} {count, plural, - =1 {torrent} - other {torrents} - } a échoué.`, - 'alert.torrent.move': `Le déplacement de {countElement} {count, plural, - =1 {torrent} - other {torrents} - } a réussi.`, - 'alert.torrent.move.failed': `Le déplacement de {countElement} {count, plural, - =1 {torrent} - other {torrents} - } a échoué.`, - 'alert.torrent.remove': `La suppression de {countElement} {count, plural, - =1 {torrent} - other {torrents} - } a réussi.`, - 'alert.torrent.remove.failed': `La suppression de {countElement} {count, plural, - =1 {torrent} - other {torrents} - } a échoué.`, - 'alert.settings.saved': 'Paramètres modifiés.', - - 'priority.dont.download': 'Arrêt', - 'priority.high': 'Haute', - 'priority.low': 'Basse', - 'priority.normal': 'Normale', - - 'settings.bandwidth.slots.download.divider.label': 'Diviseur Slots de Réception', - 'settings.bandwidth.slots.download.global.label': 'Slots de Réception (Global)', - 'settings.bandwidth.slots.download.label': 'Slots de Réception par Torrent', - 'settings.bandwidth.slots.heading': 'Configuration des Slots', - 'settings.bandwidth.slots.upload.divider.label': "Diviseur Slots d'Envoi", - 'settings.bandwidth.slots.upload.global.label': "Slots d'Envoi (Global)", - 'settings.bandwidth.slots.upload.label': "Slots d'Envoi par Torrent", - 'settings.bandwidth.transferrate.dropdown.preset.download.label': 'Pré-réglages du Menu (Réception) :', - 'settings.bandwidth.transferrate.dropdown.preset.upload.label': 'Pré-réglages du Menu (Envoi) :', - 'settings.bandwidth.transferrate.global.throttle.download': 'Limitation Globale de Réception', - 'settings.bandwidth.transferrate.global.throttle.upload': "Limitation Globale d'Envoi", - 'settings.bandwidth.transferrate.heading': 'Configuration Taux de Transfert', - - 'settings.connectivity.dht.label': 'Activer le DHT', - 'settings.connectivity.dht.port.label': 'Port DHT', - 'settings.connectivity.dpd.heading': 'Découverte de Pairs Décentralisée', - 'settings.connectivity.incoming.heading': 'Connexions Entrantes', - 'settings.connectivity.ip.hostname.label': "Adresse IP/Nom d'Hôte", - 'settings.connectivity.max.http.connections': 'Connexions HTTP Maximum', - 'settings.connectivity.peer.exchange.label': "Activer l'échange de Pairs", - 'settings.connectivity.peers.desired.label': 'Pairs Désirés', - 'settings.connectivity.peers.heading': 'Pairs', - 'settings.connectivity.peers.max.label': 'Pairs Maximum (Réception)', - 'settings.connectivity.peers.min.label': 'Pairs Minimum (Réception)', - 'settings.connectivity.peers.seeding.max.label': 'Pairs en Partage Maximum (Envoi)', - 'settings.connectivity.peers.seeding.min.label': 'Pairs en Partage Minimum (Envoi)', - 'settings.connectivity.port.open.label': 'Port Ouvert', - 'settings.connectivity.port.randomize.label': 'Port Aléatoire', - 'settings.connectivity.port.range.label': 'Plage de Ports', - - 'settings.resources.disk.check.hash.label': 'Vérifier le hash après le Téléchargement', - 'settings.resources.disk.download.location.label': 'Répertoire de Téléchargement par défaut', - 'settings.resources.disk.heading': 'Disque Dur', - 'settings.resources.max.open.files': 'Fichiers Ouverts Maximum', - 'settings.resources.memory.heading': 'Mémoire Vive', - 'settings.resources.memory.max.label': "Limite d'utilisation Mémoire Vive", - - 'settings.tabs.bandwidth': 'Bande Passante', - 'settings.tabs.connectivity': 'Connectivité', - 'settings.tabs.heading': 'Paramètres', - 'settings.tabs.resources': 'Ressources', - 'settings.tabs.authentication': 'Authentification', - 'settings.tabs.userinterface': 'Interface', - 'settings.tabs.about': 'À propos', - - 'settings.ui.locale': 'Traductions', - 'settings.ui.language': 'Langue', - - 'settings.about.flood': 'À propos de Flood', - - 'sidebar.button.feeds': 'Flux', - 'sidebar.button.settings': 'Réglages', - 'sidebar.button.speedlimits': 'Limites de Vitesse', - - 'sidebar.search.placeholder': 'Rechercher des Torrents', - - 'sidebar.transferdata.downloaded': 'Reçu', - 'sidebar.transferdata.uploaded': 'Envoyé', - - 'speed.unlimited': 'Illimité', - - 'unit.size.byte': 'o', - 'unit.size.kilobyte': 'ko', - 'unit.size.megabyte': 'Mo', - 'unit.size.gigabyte': 'Go', - 'unit.size.terabyte': 'To', - 'unit.speed': '{baseUnit}/s', - 'unit.time.year': 'an', - 'unit.time.week': 'sem', - 'unit.time.day': 'j', - 'unit.time.hour': 'h', - 'unit.time.minute': 'min', - 'unit.time.second': 's', - 'unit.time.infinity': '∞', - - 'torrents.add.button.add': 'Ajouter un Torrent', - 'torrents.add.destination.label': 'Destination', - 'torrents.add.destination.placeholder': 'Répertoire de Destination', - 'torrents.add.heading': 'Ajouter des Torrents', - 'torrents.add.start.label': 'Démarrer', - 'torrents.add.tab.file.browse': 'ou cliquez pour rechercher', - 'torrents.add.tab.file.drop': 'Glissez les fichiers ici,', - 'torrents.add.tab.file.title': 'Par Fichier', - 'torrents.add.tab.url.input.placeholder': 'URL du Torrent', - 'torrents.add.tab.url.title': 'Par URL', - 'torrents.add.torrents.label': 'Torrents', - - 'torrents.details.actions.pause': 'Pause', - 'torrents.details.actions.start': 'Démarrer', - 'torrents.details.actions.stop': 'Arrêter', - 'torrents.details.details': 'Détails', - 'torrents.details.files': 'Fichiers', - 'torrents.details.general.comment': 'Commentaires', - 'torrents.details.general.connected': '{connected} connectés sur {total}', - 'torrents.details.general.date.added': 'Ajouté', - 'torrents.details.general.date.created': 'Création', - 'torrents.details.general.downloaded': 'Téléchargé', - 'torrents.details.general.free.disk.space': 'Espace Disque Libre', - 'torrents.details.general.hash': 'Hash', - 'torrents.details.general.heading.general': 'Général', - 'torrents.details.general.heading.torrent': 'Torrent', - 'torrents.details.general.heading.tracker': 'Traqueur', - 'torrents.details.general.heading.transfer': 'Transfert', - 'torrents.details.general.location': 'Emplacement', - 'torrents.details.general.none': 'Non', - 'torrents.details.general.peers': 'Pairs', - 'torrents.details.general.scheduler.ignored': 'Ignoré', - 'torrents.details.general.scheduler.obeyed': 'Conforme', - 'torrents.details.general.scheduler': 'Planifié', - 'torrents.details.general.seeds': 'En Partage', - 'torrents.details.general.size': 'Taille', - 'torrents.details.general.tags': 'Tags', - 'torrents.details.general.tracker.message': 'Message du Traqueur', - 'torrents.details.general.type.private': 'Privé', - 'torrents.details.general.type.public': 'Public', - 'torrents.details.general.type': 'Type', - 'torrents.details.peers.no.data': "Il n'y a aucun Pair actif pour ce Torrent.", - 'torrents.details.peers': 'Pairs', - 'torrents.details.selected.files': `{count, plural, - =1 {{countElement} fichier sélectionné} - other {{countElement} fichiers sélectionnés} - }`, - 'torrents.details.selected.files.set.priority': 'Priorité', - 'torrents.details.trackers.no.data': "Il n'y a aucun Traqueur actif pour ce Torrent.", - 'torrents.details.trackers.type': 'Type', - 'torrents.details.trackers': 'Traqueurs', - - 'torrents.list.clear.filters': 'Vider les Filtres', - 'torrents.list.context.check.hash': 'Vérifier le Hash', - 'torrents.list.context.move': 'Déplacer', - 'torrents.list.context.pause': 'Pause', - 'torrents.list.context.priority': 'Priorité', - 'torrents.list.context.remove': 'Supprimer', - 'torrents.list.context.set.tags': 'Définir un Tag', - 'torrents.list.context.start': 'Démarrer', - 'torrents.list.context.stop': 'Arrêter', - 'torrents.list.no.torrents': 'Aucun Torrent à afficher.', - 'torrent.list.peers': '{connected} {of} {total}', - 'torrent.list.peers.of': 'sur', - - 'torrents.move.button.set.location': 'Emplacement', - 'torrents.move.button.state.setting': 'Réglages...', - 'torrents.move.data.label': 'Déplacer les Données', - 'torrents.move.heading': "Définir l'Emplacement de Téléchargement", - - 'torrents.properties.base.path': 'Chemin absolu', - 'torrents.properties.comment': 'Commentaire', - 'torrents.properties.creation.date': 'Date de Création', - 'torrents.properties.date.added': "Date d'Ajout", - 'torrents.properties.download.speed': 'Vitesse de Téléchargement', - 'torrents.properties.download.total': 'Données Reçues', - 'torrents.properties.eta': 'Date de fin estimée', - 'torrents.properties.free.disk.space': 'Espace libre', - 'torrents.properties.hash': 'Hash', - 'torrents.properties.ignore.schedule': 'Ignorer le Planificateur', - 'torrents.properties.is.private': 'Privé', - 'torrents.properties.name': 'Nom des Fichiers', - 'torrents.properties.percentage': 'Pourcentage Complété', - 'torrents.properties.ratio': 'Ratio', - 'torrents.properties.size': 'Taille des Fichiers', - 'torrents.properties.tags': 'Tags', - 'torrents.properties.tracker.message': 'Message du Tracker', - - 'torrents.properties.upload.speed': "Vitesse d'Envoi", - 'torrents.properties.upload.total': 'Données Envoyées', - - 'torrents.remove.are.you.sure': `Êtes-vous certain(e) de vouloir supprimer {count, plural, - =0 {aucun torrent} - =1 {un torrent} - other {# torrents} - } ?`, - 'torrents.remove.delete.data': 'Supprimer les données', - 'torrents.remove.error.no.torrents.selected': "Vous n'avez sélectionné aucun torrent.", - 'torrents.remove': 'Supprimer les Torrents', - - 'torrents.set.tags.button.set': 'Définir des Tags', - 'torrents.set.tags.heading': 'Définir des Tags', - 'torrents.set.tags.enter.tags': 'Entrer des Tags', - - 'torrents.sort.title': 'Trier par', -}; diff --git a/client/src/javascript/i18n/ko.js b/client/src/javascript/i18n/ko.js deleted file mode 100644 index 8360b946b..000000000 --- a/client/src/javascript/i18n/ko.js +++ /dev/null @@ -1,354 +0,0 @@ -export default { - 'alert.torrent.add': `{countElement} {count, plural, - =1 {토렌트} - other {토렌트} - }를 추가했습니다.`, - 'alert.torrent.add.failed': `{countElement} {count, plural, - =1 {토렌트} - other {토렌트} - }를 추가하지 못했습니다.`, - 'alert.torrent.move': `{countElement} {count, plural, - =1 {토렌트} - other {토렌트} - }를 이동했습니다.`, - 'alert.torrent.move.failed': `{countElement} {count, plural, - =1 {토렌트} - other {토렌트} - }를 이동하지 못했습니다.`, - 'alert.torrent.remove': `{countElement} {count, plural, - =1 {토렌트} - other {토렌트} - }를 제거했습니다.`, - 'alert.torrent.remove.failed': `{countElement} {count, plural, - =1 {토렌트} - other {토렌트} - }를 제거하지 못했습니다.`, - 'alert.settings.saved': '설정을 저장했습니다.', - - 'auth.add.user': '사용자 추가', - 'auth.create.account': '계정 생성', - 'auth.create.an.account': '계정 생성', - 'auth.create.an.account.intro': '환영합니다, Flood입니다!', - 'auth.current.user': '현재 사용자', - 'auth.error.username.empty': '사용자 이름은 비어있을 수 없습니다.', - 'auth.log.in': '로그인', - 'auth.login': '로그인', - 'auth.login.intro': '당신의 계정으로 로그인하세요.', - 'auth.password': '비밀번호', - 'auth.user.accounts': '사용자 계정', - 'auth.username': '사용자 이름', - 'auth.admin': '관리자', - 'auth.message.not.admin': '관리자가 아닌 사용자입니다', - 'auth.rtorrentHost': 'rTorrent 호스트', - 'auth.rtorrentPort': 'rTorrent 포트', - 'auth.rtorrentSocket': 'rTorrent 소켓', - 'auth.rtorrentSocketPath': 'rTorrent 소켓 경로', - 'auth.connectionType': 'rTorrent 연결 종류', - 'auth.connectionType.tcp': 'TCP', - 'auth.connectionType.socket': '유닉스 소켓', - - 'button.add': '추가', - 'button.cancel': '취소', - 'button.no': '아니오', - 'button.save': '설정 저장', - 'button.test': '테스트', - 'button.state.adding': '추가 중...', - 'button.yes': '예', - - 'connectivity.modal.title': '연결 문제', - 'connectivity.modal.content': 'rTorrent에 연결하지 못했습니다. 지금, 설정을 고쳐주세요.', - - 'feeds.add.automatic.download.rule': '다운로드 규칙 추가', - 'feeds.add.feed': '피드 추가', - 'feeds.applicable.feed': '가용 피드', - 'feeds.apply.tags': '태그 적용', - 'feeds.exclude.pattern': '제외 패턴', - 'feeds.existing.feeds': '설정된 피드', - 'feeds.existing.rules': '설정된 규칙', - 'feeds.label': '라벨', - 'feeds.match.count': '{count, plural, =1 {# 일치} other {# 일치}}', - 'feeds.match.pattern': '일치 패턴', - 'feeds.match': '일치', - 'feeds.exclude': '제외', - 'feeds.no.feeds.available': '사용할 수 있는 피드가 없습니다.', - 'feeds.no.feeds.defined': '지정된 피드가 없습니다.', - 'feeds.no.rules.defined': '지정된 규칙이 없습니다.', - 'feeds.regEx': '정규 표현식', - 'feeds.select.feed': '피드 선택', - 'feeds.select.interval': '간격', - 'feeds.interval': '간격', - 'feeds.start.on.load': '시작 시 불러오기', - 'feeds.tabs.download.rules': '다운로드 규칙', - 'feeds.tabs.feeds': '피드', - 'feeds.tabs.heading': '토렌트 피드', - 'feeds.tags': '태그', - 'feeds.time.hr': '{durationValue} 시간', - 'feeds.time.min': '{durationValue} 분', - 'feeds.torrent.destination': '토렌트 저장 경로', - 'feeds.url': 'URL', - 'feeds.validation.invalid.regular.expression': '정규 표현식이 잘못되었습니다.', - 'feeds.validation.must.select.feed': '피드를 선택해야 합니다.', - 'feeds.validation.must.specify.destination': '저장할 경로를 지정해야 합니다.', - 'feeds.validation.must.specify.label': '라벨을 지정해야 합니다.', - 'feeds.validation.must.specify.valid.feed.url': '올바른 피드 URL을 지정해야 합니다.', - - 'filesystem.empty.directory': '비어있는 디렉토리입니다.', - 'filesystem.error.eacces': 'Flood에 이 디렉토리를 읽을 권한이 없습니다.', - 'filesystem.error.enoent': '없는 경로입니다. 생성될 것입니다.', - 'filesystem.fetching': '디렉토리 구조 가져오는 중...', - - 'filter.all': '전체', - 'filter.status.title': '필터: 상태', - 'filter.status.downloading': '다운로드 중', - 'filter.status.completed': '완료', - 'filter.status.stopped': '중지', - 'filter.status.active': '활성', - 'filter.status.inactive': '비활성', - 'filter.status.error': '오류', - 'filter.tracker.title': '필터: 트래커', - 'filter.tag.title': '필터: 태그', - 'filter.untagged': '태그 없음', - - 'general.ago': '전', - 'general.at': '@', - 'general.to': '-', - 'general.of': '/', - - 'general.clipboard.copy': '복사', - 'general.clipboard.copied': '복사됨', - - 'locale.language.de': 'Deutsch', - 'locale.language.en': 'English', - 'locale.language.es': 'Español', - 'locale.language.fr': 'Français', - 'locale.language.ko': '한국어', - 'locale.language.nl': 'Nederlands', - - 'mediainfo.execError': - '서버에서 미디어 정보를 가져오는 중 오류가 발생했습니다. mediainfo가 설치되어 있으며 Flood가 쓸 수 있도록 PATH가 지정되어 있는지 확인하세요.', - 'mediainfo.fetching': '가져오는 중...', - 'mediainfo.heading': 'Mediainfo 출력', - - 'notification.torrent.finished.heading': '다운로드 완료', - 'notification.torrent.finished.body': '{name}', - 'notification.torrent.errored.heading': '오류 보고', - 'notification.torrent.errored.body': '{name}', - 'notification.clear.all': '모두 지우기', - 'notification.showing': '표시 중', - - 'priority.dont.download': '다운로드하지 않음', - 'priority.high': '높음', - 'priority.low': '낮음', - 'priority.normal': '보통', - - 'settings.bandwidth.slots.download.divider.label': '다운로드 슬롯 나눌 값', - 'settings.bandwidth.slots.download.global.label': '전체 다운로드 슬롯 개수', - 'settings.bandwidth.slots.download.label': '토렌트당 다운로드 슬롯 개수', - 'settings.bandwidth.slots.heading': '가용 슬롯', - 'settings.bandwidth.slots.upload.divider.label': '업로드 슬롯 나눌 값', - 'settings.bandwidth.slots.upload.global.label': '전체 업로드 슬롯 개수', - 'settings.bandwidth.slots.upload.label': '토렌트당 업로드 슬롯 개수', - 'settings.bandwidth.transferrate.dropdown.preset.download.label': '드롭다운 메뉴 속도 목록: 다운로드', - 'settings.bandwidth.transferrate.dropdown.preset.upload.label': '드롭다운 메뉴 속도 목록: 업로드', - 'settings.bandwidth.transferrate.global.throttle.download': '전체 다운로드 속도 제한', - 'settings.bandwidth.transferrate.global.throttle.upload': '전체 업로드 속도 제한', - 'settings.bandwidth.transferrate.heading': '전송 속도 제한', - - 'settings.connectivity.dht.label': 'DHT 사용', - 'settings.connectivity.dht.port.label': 'DHT 포트', - 'settings.connectivity.dpd.heading': '탈중앙 피어 탐색', - 'settings.connectivity.incoming.heading': '들어오는 연결', - 'settings.connectivity.ip.hostname.label': '보고된 IP/호스트명', - 'settings.connectivity.max.http.connections': '최대 HTTP 연결', - 'settings.connectivity.peer.exchange.label': '피어 교환 사용', - 'settings.connectivity.peers.desired.label': '희망 피어 수', - 'settings.connectivity.peers.heading': '피어', - 'settings.connectivity.peers.max.label': '최대 피어 수', - 'settings.connectivity.peers.min.label': '최소 피어 수', - 'settings.connectivity.peers.seeding.max.label': '시딩할 최대 피어 수', - 'settings.connectivity.peers.seeding.min.label': '시딩할 최소 피어 수', - 'settings.connectivity.port.open.label': '포트 열기', - 'settings.connectivity.port.randomize.label': '임의 포트', - 'settings.connectivity.port.range.label': '포트 범위', - - 'settings.resources.disk.check.hash.label': '완료 시 해시 검증', - 'settings.resources.disk.download.location.label': '기본 다운로드 경로', - 'settings.resources.disk.heading': '디스크', - 'settings.resources.max.open.files': '최대 열어둘 파일 개수', - 'settings.resources.memory.heading': '메모리', - 'settings.resources.memory.max.label': '최대 메모리 사용량', - - 'settings.tabs.bandwidth': '대역폭', - 'settings.tabs.connectivity': '연결', - 'settings.tabs.heading': '설정', - 'settings.tabs.resources': '자원', - 'settings.tabs.authentication': '인증', - 'settings.tabs.userinterface': '사용자 인터페이스', - - 'settings.ui.locale': '지역', - 'settings.ui.language': '언어', - 'settings.ui.torrent.list': '토렌트 목록 표시', - 'settings.ui.torrent.size': '토렌트 크기', - 'settings.ui.torrent.size.expanded': '확장 보기', - 'settings.ui.torrent.size.condensed': '간략히 보기', - 'settings.ui.torrent.displayed.details': '토렌트 속성 열', - 'settings.ui.torrent.details.tags.placement': '확장 보기에서는, 태그를 목록 끝에 배치하면 제일 잘 작동합니다.', - 'settings.ui.torrent.details.enabled': '표시', - - 'sidebar.button.feeds': '피드', - 'sidebar.button.notifications': '알림', - 'sidebar.button.settings': '설정', - 'sidebar.button.speedlimits': '속도 제한', - 'sidebar.button.log.out': '로그아웃', - - 'sidebar.search.placeholder': '토렌트 검색', - - 'sidebar.transferdata.downloaded': '다운로드한 크기', - 'sidebar.transferdata.uploaded': '업로드한 크기', - - 'actionbar.button.start.torrent': '토렌트 시작', - 'actionbar.button.stop.torrent': '토렌트 중지', - 'actionbar.button.add.torrent': '토렌트 추가', - 'actionbar.button.remove.torrent': '토렌트 제거', - - 'speed.unlimited': '무제한', - - 'unit.size.byte': 'B', - 'unit.size.kilobyte': 'kB', - 'unit.size.megabyte': 'MB', - 'unit.size.gigabyte': 'GB', - 'unit.size.terabyte': 'TB', - 'unit.speed': '{baseUnit}/s', - 'unit.time.year': '년', - 'unit.time.week': '주', - 'unit.time.day': '일', - 'unit.time.hour': '시', - 'unit.time.minute': '분', - 'unit.time.second': '초', - 'unit.time.infinity': '∞', - - 'torrents.add.button.add': '토렌트 추가', - 'torrents.add.destination.label': '저장 경로', - 'torrents.add.destination.placeholder': '저장 경로', - 'torrents.add.heading': '토렌트 추가', - 'torrents.add.start.label': '토렌트 시작', - 'torrents.add.tab.file.browse': '또는 클릭해서 파일 열기', - 'torrents.add.tab.file.drop': '파일을 여기로 끌어넣거나,', - 'torrents.add.tab.file.title': '파일', - 'torrents.add.tab.url.input.placeholder': '토렌트 URL 또는 마그넷 주소', - 'torrents.add.tab.url.title': 'URL', - 'torrents.add.torrents.label': '토렌트', - - 'torrents.destination.base_path': '기본 경로로 사용', - - 'torrents.details.actions.pause': '일시중지', - 'torrents.details.actions.start': '시작', - 'torrents.details.actions.stop': '중지', - 'torrents.details.details': '속성', - 'torrents.details.files': '파일', - 'torrents.details.files.download.file': `{count, plural, - =1 {선택한 파일 다운로드} - other {선택한 파일 다운로드} - }`, - 'torrents.details.general.comment': '설명', - 'torrents.details.general.connected': '{total} 중 {connected} 연결됨', - 'torrents.details.general.date.added': '추가한 날', - 'torrents.details.general.date.created': '만든 날', - 'torrents.details.general.downloaded': '다운로드한 크기', - 'torrents.details.general.free.disk.space': '남은 디스크 공간', - 'torrents.details.general.hash': '해시', - 'torrents.details.general.heading.general': '일반', - 'torrents.details.general.heading.torrent': '토렌트', - 'torrents.details.general.heading.tracker': '트래커', - 'torrents.details.general.heading.transfer': '전송', - 'torrents.details.general.location': '다운로드 경로', - 'torrents.details.general.none': '없음', - 'torrents.details.general.peers': '피어', - 'torrents.details.general.scheduler.ignored': '무시', - 'torrents.details.general.scheduler.obeyed': '준수', - 'torrents.details.general.scheduler': '스케줄러', - 'torrents.details.general.seeds': '시드', - 'torrents.details.general.size': '크기', - 'torrents.details.general.tags': '태그', - 'torrents.details.general.tracker.message': '트래커 메시지', - 'torrents.details.general.type.private': '비공개', - 'torrents.details.general.type.public': '공개', - 'torrents.details.general.type': '종류', - 'torrents.details.mediainfo': '미디어 정보', - 'torrents.details.peers.no.data': '이 토렌트에는 피어 정보가 없습니다.', - 'torrents.details.peers': '피어', - 'torrents.details.files.loading': '파일 목록 불러오는 중...', - 'torrents.details.selected.files': `{count, plural, - =1 {{countElement} 파일 선택} - other {{countElement} 파일 선택} - }`, - 'torrents.details.selected.files.set.priority': '우선순위 지정', - 'torrents.details.trackers.no.data': '이 토렌트에는 트래커 정보가 없습니다.', - 'torrents.details.trackers.type': '종류', - 'torrents.details.trackers': '트래커', - - 'torrents.list.clear.filters': '필터 지우기', - 'torrents.list.context.check.hash': '해시 검사', - 'torrents.list.context.details': '토렌트 속성', - 'torrents.list.context.download': '다운로드', - 'torrents.list.context.move': '토렌트 위치 지정', - 'torrents.list.context.pause': '일시중지', - 'torrents.list.context.priority': '우선순위', - 'torrents.list.context.remove': '제거', - 'torrents.list.context.set.tags': '태그 지정', - 'torrents.list.context.start': '시작', - 'torrents.list.context.stop': '중지', - 'torrents.list.no.torrents': '표시할 토렌트가 없습니다.', - 'torrents.list.drop': '여기로 파일을 끌어넣어 rTorrent에 추가하세요.', - 'torrents.list.cannot.connect': 'rTorrent에 연결하지 못했습니다.', - 'torrent.list.peers': '{total} {of} {connected}', - 'torrent.list.peers.of': '중', - - 'torrents.move.button.set.location': '경로 지정', - 'torrents.move.button.state.setting': '이동 중...', - 'torrents.move.data.label': '데이터 이동', - 'torrents.move.heading': '토렌트 경로 지정', - - 'torrents.properties.date.added': '추가한 날', - 'torrents.properties.base.path': '기본 경로', - 'torrents.properties.comment': '설명', - 'torrents.properties.creation.date': '만든 날', - 'torrents.properties.download.speed': '다운로드 속도', - 'torrents.properties.download.total': '다운로드한 크기', - 'torrents.properties.eta': '예상 남은 시간', - 'torrents.properties.free.disk.space': '남은 디스크 공간', - 'torrents.properties.hash': '해시', - 'torrents.properties.ignore.schedule': '스케줄러 무시', - 'torrents.properties.is.private': '비공개', - 'torrents.properties.name': '이름', - 'torrents.properties.peers': '피어', - 'torrents.properties.percentage': '완료율', - 'torrents.properties.ratio': '비율', - 'torrents.properties.seeds': '시드', - 'torrents.properties.size': '파일 크기', - 'torrents.properties.tags': '태그', - 'torrents.properties.tracker.message': '트래커 메시지', - 'torrents.properties.trackers': '트래커', - 'torrents.properties.upload.speed': '업로드 속도', - 'torrents.properties.upload.total': '업로드 크기', - - 'torrents.remove.are.you.sure': `정말 {count, plural, - =0 {0개 토렌트를} - =1 {토렌트 한 개를} - other {#개 토렌트를} - } 제거할까요?`, - 'torrents.remove.delete.data': '데이터 삭제', - 'torrents.remove.error.no.torrents.selected': '토렌트를 선택하지 않았습니다.', - 'torrents.remove': '토렌트 제거', - - 'torrents.set.tags.button.set': '태그 지정', - 'torrents.set.tags.heading': '태그 지정', - 'torrents.set.tags.enter.tags': '태그 입력', - - 'torrents.sort.title': '정렬 기준', - - 'connection-interruption.heading': 'rTorrent에 연결하지 못했습니다', - 'connection-interruption.verify-settings-prompt': '연결 설정을 검증해보겠습니다.', - 'connection-interruption.verification-error': '연결을 검증하지 못했습니다.', - 'connection-interruption.verification-success': '연결 성공', -}; diff --git a/client/src/javascript/i18n/languages.ts b/client/src/javascript/i18n/languages.ts deleted file mode 100644 index 6e599ca21..000000000 --- a/client/src/javascript/i18n/languages.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {addLocaleData} from 'react-intl'; -import deLocaleData from 'react-intl/locale-data/de'; -import enLocaleData from 'react-intl/locale-data/en'; -import esLocaleData from 'react-intl/locale-data/es'; -import frLocaleData from 'react-intl/locale-data/fr'; -import koLocaleData from 'react-intl/locale-data/ko'; -import nlLocaleData from 'react-intl/locale-data/nl'; -import zhLocaleData from 'react-intl/locale-data/zh'; - -addLocaleData(deLocaleData); -addLocaleData(enLocaleData); -addLocaleData(esLocaleData); -addLocaleData(frLocaleData); -addLocaleData(koLocaleData); -addLocaleData(nlLocaleData); -addLocaleData(zhLocaleData); - -export {default as de} from './de'; -export {default as en} from './en'; -export {default as es} from './es'; -export {default as fr} from './fr'; -export {default as ko} from './ko'; -export {default as nl} from './nl'; -export {default as zh} from './zh'; diff --git a/client/src/javascript/i18n/languages.tsx b/client/src/javascript/i18n/languages.tsx new file mode 100644 index 000000000..c0255cc7d --- /dev/null +++ b/client/src/javascript/i18n/languages.tsx @@ -0,0 +1,76 @@ +import {IntlProvider} from 'react-intl'; +import * as React from 'react'; + +import type {MessageFormatElement} from 'intl-messageformat-parser'; + +import detectLocale from '../util/detectLocale'; +import EN from './strings.compiled.json'; +import Languages from '../constants/Languages'; + +import type {Language} from '../constants/Languages'; +import type {LocaleConfig} from '../util/detectLocale'; + +const messagesCache: Partial, Record>> = {en: EN}; + +const inContextTranslator = document.createElement('script'); +inContextTranslator.src = '//cdn.crowdin.com/jipt/jipt.js'; + +function loadTranslator() { + if (!document.head.contains(inContextTranslator)) { + document.head.appendChild(inContextTranslator); + } +} + +async function loadMessages(locale: Exclude) { + const messages: Record = await import( + /* webpackChunkName: 'i18n' */ + `./compiled/${locale === 'translate' ? 'en' : locale}.json` + ); + + messagesCache[locale] = messages; + + return messages; +} + +function getMessages(locale: Exclude) { + if (locale === 'translate') { + loadTranslator(); + } + + if (messagesCache[locale]) { + return messagesCache[locale]; + } + + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw loadMessages(locale as Exclude); +} + +interface AsyncIntlProviderProps { + language?: Language; + children: React.ReactNode; +} + +const AsyncIntlProvider: React.FC = ({language, children}: AsyncIntlProviderProps) => { + let validatedLocale: LocaleConfig; + if (language == null || language === 'auto' || !Object.prototype.hasOwnProperty.call(Languages, language)) { + validatedLocale = detectLocale(); + } else { + validatedLocale = { + locale: language, + language, + }; + } + + const messages = getMessages(validatedLocale.language); + return ( + + {children} + + ); +}; + +AsyncIntlProvider.defaultProps = { + language: 'en', +}; + +export default AsyncIntlProvider; diff --git a/client/src/javascript/i18n/nl.js b/client/src/javascript/i18n/nl.js deleted file mode 100644 index fa6cf0fcd..000000000 --- a/client/src/javascript/i18n/nl.js +++ /dev/null @@ -1,266 +0,0 @@ -export default { - 'auth.add.user': 'Gebruiker toevoegen', - 'auth.create.account': 'Maak account', - 'auth.create.an.account': 'Maak een account', - 'auth.error.username.empty': 'Gebruikersnaam mag niet leeg zijn.', - 'auth.log.in': 'Log in', - 'auth.login': 'Login', - 'auth.password': 'Wachtwoord', - 'auth.user.accounts': 'Accounts', - 'auth.username': 'Gebruikersnaam', - 'auth.admin': 'Beheerder', - 'auth.message.not.admin': 'Gebruiker is geen Beheerder', - - 'button.add': 'Toevoegen', - 'button.cancel': 'Annuleren', - 'button.no': 'Nee', - 'button.save.feed': 'Opslaan', - 'button.save': 'Instellingen opslaan', - 'button.state.adding': 'Toevoegen...', - 'button.yes': 'Ja', - - 'feeds.add.automatic.download.rule': 'Regel toevoegen', - 'feeds.add.feed': 'Feed toevoegen', - 'feeds.applicable.feed': 'Feed', - 'feeds.apply.tags': 'Tags toevoegen', - 'feeds.exclude.pattern': 'Overslaan op patroon', - 'feeds.existing.feeds': 'Feeds', - 'feeds.existing.rules': 'Regels', - 'feeds.label': 'Label', - 'feeds.match.count': '{count, plural, =1 {# match} other {# matches}}', - 'feeds.match.pattern': 'Match op patroon', - 'feeds.match': 'Match', - 'feeds.no.feeds.available': 'Geen feeds beschikbaar.', - 'feeds.no.feeds.defined': 'Geen feeds toegevoegd.', - 'feeds.no.rules.defined': 'Geen regels toegevoegd.', - 'feeds.regEx': 'RegEx', - 'feeds.select.feed': 'Selecteer feed', - 'feeds.select.interval': 'Interval', - 'feeds.start.on.load': 'Start na toevoegen', - 'feeds.tabs.download.rules': 'Regels', - 'feeds.tabs.feeds': 'Feeds', - 'feeds.tabs.heading': 'Torrent feeds', - 'feeds.tags': 'Tags', - 'feeds.time.hr': '{durationValue} uur', - 'feeds.time.min': '{durationValue} min', - 'feeds.torrent.destination': 'Torrent downloadlocatie', - 'feeds.url': 'URL', - 'feeds.validation.invalid.regular.expression': 'Fout in regular expression.', - 'feeds.validation.must.select.feed': 'Je moet een feed selecteren.', - 'feeds.validation.must.specify.destination': 'Je moet een downloadlocatie opgeven.', - 'feeds.validation.must.specify.label': 'Je moet een label selecteren.', - 'feeds.validation.must.specify.valid.feed.url': 'Je moet een geldige feed URL opgeven.', - - 'filter.all': 'Alles', - 'filter.status.title': 'Filter op status', - 'filter.status.downloading': 'Downloaden', - 'filter.status.completed': 'Klaar', - 'filter.status.active': 'Actief', - 'filter.status.inactive': 'Inactief', - 'filter.status.error': 'Fout', - 'filter.tracker.title': 'Filter op tracker', - 'filter.tag.title': 'Filter op tag', - 'filter.untagged': 'Niet getagd', - - 'locale.language.en': 'English', - 'locale.language.fr': 'Frans', - 'locale.language.ko': 'Koreaans', - 'locale.language.nl': 'Nederlands', - - 'alert.torrent.add': `Het toevoegen van {countElement} {count, plural, - =1 {torrent} - other {torrents} - } is klaar.`, - 'alert.torrent.add.failed': `Het toevoegen van {countElement} {count, plural, - =1 {torrent} - other {torrents} - } is mislukt.`, - 'alert.torrent.move': `Het verplaatsen van {countElement} {count, plural, - =1 {torrent} - other {torrents} - } is klaar.`, - 'alert.torrent.move.failed': `Het verplaatsen van {countElement} {count, plural, - =1 {torrent} - other {torrents} - } is mislukt.`, - 'alert.torrent.remove': `Het verwijderen van {countElement} {count, plural, - =1 {torrent} - other {torrents} - } is klaar.`, - 'alert.torrent.remove.failed': `Het verwijderen van {countElement} {count, plural, - =1 {torrent} - other {torrents} - } is mislukt.`, - 'alert.settings.saved': 'Instellingen zijn opgeslagen.', - - 'priority.dont.download': 'Niet downloaden', - 'priority.high': 'Hoog', - 'priority.low': 'Laag', - 'priority.normal': 'Normaal', - - 'settings.bandwidth.slots.download.divider.label': 'Download slots divider', - 'settings.bandwidth.slots.download.global.label': 'Download slots globaal', - 'settings.bandwidth.slots.download.label': 'Download slots per torrent', - 'settings.bandwidth.slots.heading': 'Slots', - 'settings.bandwidth.slots.upload.divider.label': 'Upload slots divider', - 'settings.bandwidth.slots.upload.global.label': 'Upload slots globaal', - 'settings.bandwidth.slots.upload.label': 'Upload slots per torrent', - 'settings.bandwidth.transferrate.dropdown.preset.download.label': 'Dropdown downloadbeperking items', - 'settings.bandwidth.transferrate.dropdown.preset.upload.label': 'Dropdown uploadbeperking items', - 'settings.bandwidth.transferrate.global.throttle.download': 'Globale downloadbeperking', - 'settings.bandwidth.transferrate.global.throttle.upload': 'Gobale uploadbeperking', - 'settings.bandwidth.transferrate.heading': 'Sneldheidsbeperkingen', - - 'settings.connectivity.dht.label': 'Gebruik DHT', - 'settings.connectivity.dht.port.label': 'DHT Poort', - 'settings.connectivity.dpd.heading': 'Decentralized Peer Discovery', - 'settings.connectivity.incoming.heading': 'Inkomende connecties', - 'settings.connectivity.ip.hostname.label': 'Vermeld IP/Hostnaam', - 'settings.connectivity.max.http.connections': 'Maximaal aantal HTTP connecties', - 'settings.connectivity.peer.exchange.label': 'Gebruik Peer Exchange', - 'settings.connectivity.peers.desired.label': 'Gewenst aantal peers', - 'settings.connectivity.peers.heading': 'Peers', - 'settings.connectivity.peers.max.label': 'Maximaal aantal peers', - 'settings.connectivity.peers.min.label': 'Minimaal aantal peers', - 'settings.connectivity.peers.seeding.max.label': 'Maximaal aantal peers seeding', - 'settings.connectivity.peers.seeding.min.label': 'Minimaal aantal peers seeding', - 'settings.connectivity.port.open.label': 'Open poort', - 'settings.connectivity.port.randomize.label': 'Willekeurige poort', - 'settings.connectivity.port.range.label': 'Poortbereik', - - 'settings.resources.disk.check.hash.label': 'Controleer hash na downloaden', - 'settings.resources.disk.download.location.label': 'Standaard downloadlocatie', - 'settings.resources.disk.heading': 'Disk', - 'settings.resources.max.open.files': 'Maximaal aantal open bestanden', - 'settings.resources.memory.heading': 'Geheugen', - 'settings.resources.memory.max.label': 'Maximaal geheugengebruik', - - 'settings.tabs.bandwidth': 'Bandbreedte', - 'settings.tabs.connectivity': 'Connectiviteit', - 'settings.tabs.heading': 'Instellingen', - 'settings.tabs.resources': 'Resources', - 'settings.tabs.authentication': 'Authenticatie', - 'settings.tabs.userinterface': 'Gebruikersomgeving', - - 'settings.ui.locale': 'Landinstelling', - 'settings.ui.language': 'Taal', - - 'sidebar.button.feeds': 'Feeds', - 'sidebar.button.settings': 'Instellingen', - 'sidebar.button.speedlimits': 'Sneldheidsbeperkingen', - - 'sidebar.search.placeholder': 'Zoek torrents', - - 'sidebar.transferdata.downloaded': 'Gedownload', - 'sidebar.transferdata.uploaded': 'Geupload', - - 'speed.unlimited': 'Ongelimiteerd', - - 'unit.size.byte': 'B', - 'unit.size.kilobyte': 'kB', - 'unit.size.megabyte': 'MB', - 'unit.size.gigabyte': 'GB', - 'unit.size.terabyte': 'TB', - 'unit.speed': '{baseUnit}/s', - 'unit.time.year': 'j', - 'unit.time.week': 'w', - 'unit.time.day': 'd', - 'unit.time.hour': 't', - 'unit.time.minute': 'm', - 'unit.time.second': 'tw', - 'unit.time.infinity': '∞', - - 'torrents.add.button.add': 'Torrent toevoegen', - 'torrents.add.destination.label': 'Downloadlocatie', - 'torrents.add.destination.placeholder': 'Downloadlocatie', - 'torrents.add.heading': 'Torrents toevoegen', - 'torrents.add.start.label': 'Start torrent', - 'torrents.add.tab.file.browse': 'of klik om te bladeren.', - 'torrents.add.tab.file.drop': 'Sleep bestanden hierheen,', - 'torrents.add.tab.file.title': 'Via bestand', - 'torrents.add.tab.url.input.placeholder': 'Torrent URL', - 'torrents.add.tab.url.title': 'Via URL', - 'torrents.add.torrents.label': 'Torrents', - - 'torrents.details.actions.pause': 'Pauzeer', - 'torrents.details.actions.start': 'Start', - 'torrents.details.actions.stop': 'Stop', - 'torrents.details.details': 'Details', - 'torrents.details.files': 'Bestanden', - 'torrents.details.general.comment': 'Opmerking', - 'torrents.details.general.connected': '{connected} verbonden van {total}', - 'torrents.details.general.date.added': 'Toegevoegd op', - 'torrents.details.general.date.created': 'Aangemaakt op', - 'torrents.details.general.downloaded': 'Gedownload', - 'torrents.details.general.free.disk.space': 'Vrije schijfruimte', - 'torrents.details.general.hash': 'Hash', - 'torrents.details.general.heading.general': 'Algemeen', - 'torrents.details.general.heading.torrent': 'Torrent', - 'torrents.details.general.heading.tracker': 'Tracker', - 'torrents.details.general.heading.transfer': 'Overdracht', - 'torrents.details.general.location': 'Downloadlocatie', - 'torrents.details.general.none': 'Geen', - 'torrents.details.general.peers': 'Peers', - 'torrents.details.general.scheduler.ignored': 'Genegeerd', - 'torrents.details.general.scheduler.obeyed': 'Uitgevoerd', - 'torrents.details.general.scheduler': 'Scheduler', - 'torrents.details.general.seeds': 'Seeds', - 'torrents.details.general.size': 'Grootte', - 'torrents.details.general.tags': 'Tags', - 'torrents.details.general.tracker.message': 'Bericht van tracker', - 'torrents.details.general.type.private': 'Privé', - 'torrents.details.general.type.public': 'Publiek', - 'torrents.details.general.type': 'Type', - 'torrents.details.peers.no.data': 'Er zijn geen peer-gegevens voor deze torrent.', - 'torrents.details.peers': 'Peers', - 'torrents.details.selected.files': `{count, plural, - =1 {{countElement} bestand geselecteerd} - other {{countElement} bestanden geselecteerd} - }`, - 'torrents.details.selected.files.set.priority': 'Wijzig prioriteit', - 'torrents.details.trackers.no.data': 'Er zijn geen tracker-gegevens voor deze torrent.', - 'torrents.details.trackers.type': 'Type', - 'torrents.details.trackers': 'Trackers', - - 'torrents.list.clear.filters': 'Herstel filters', - 'torrents.list.context.check.hash': 'Controleer hash', - 'torrents.list.context.move': 'Downloadlocatie...', - 'torrents.list.context.pause': 'Pauzeer', - 'torrents.list.context.priority': 'Prioriteit', - 'torrents.list.context.remove': 'Verwijder', - 'torrents.list.context.set.tags': 'Tags instellen', - 'torrents.list.context.start': 'Start', - 'torrents.list.context.stop': 'Stop', - 'torrents.list.no.torrents': 'Geen torrents om te laten zien.', - 'torrent.list.peers': '{connected} {of} {total}', - 'torrent.list.peers.of': 'van', - - 'torrents.move.button.set.location': 'Wijzig downloadlocatie', - 'torrents.move.button.state.setting': 'Wijzigen...', - 'torrents.move.data.label': 'Verplaats bestanden', - 'torrents.move.heading': 'Wijzig downloadlocatie', - - 'torrents.properties.date.added': 'Datum toegevoegd', - 'torrents.properties.download.speed': 'Downloadsnelheid', - 'torrents.properties.download.total': 'Gedownload', - 'torrents.properties.eta': 'ETA', - 'torrents.properties.name': 'Naam', - 'torrents.properties.percentage': 'Percentage compleet', - 'torrents.properties.ratio': 'Ratio', - 'torrents.properties.size': 'Bestandsgrootte', - 'torrents.properties.upload.speed': 'Uploadsnelheid', - 'torrents.properties.upload.total': 'Geupload', - - 'torrents.remove.are.you.sure': `Weet je zeker dat je {count, plural, - =0 {geen torrents} - =1 {één torrent} - other {# torrents} - } wilt verwijderen?`, - 'torrents.remove.error.no.torrents.selected': 'Je hebt geen torrents geselecteerd.', - 'torrents.remove': 'Torrents verwijderen', - - 'torrents.set.tags.button.set': 'Tags instellen', - 'torrents.set.tags.heading': 'Tags instellen', - - 'torrents.sort.title': 'Sorteer op', -}; diff --git a/client/src/javascript/i18n/strings.compiled.json b/client/src/javascript/i18n/strings.compiled.json new file mode 100644 index 000000000..876af72fd --- /dev/null +++ b/client/src/javascript/i18n/strings.compiled.json @@ -0,0 +1,2628 @@ +{ + "actionbar.button.add.torrent": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "actionbar.button.remove.torrent": [ + { + "type": 0, + "value": "Remove Torrent" + } + ], + "actionbar.button.start.torrent": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "actionbar.button.stop.torrent": [ + { + "type": 0, + "value": "Stop Torrent" + } + ], + "alert.settings.saved": [ + { + "type": 0, + "value": "Successfully saved settings." + } + ], + "alert.torrent.add": [ + { + "type": 0, + "value": "Successfully added " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.add.failed": [ + { + "type": 0, + "value": "Failed to add " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move": [ + { + "type": 0, + "value": "Successfully moved " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.move.failed": [ + { + "type": 0, + "value": "Failed to move " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove": [ + { + "type": 0, + "value": "Successfully removed " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "alert.torrent.remove.failed": [ + { + "type": 0, + "value": "Failed to remove " + }, + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "." + } + ], + "auth.add.user": [ + { + "type": 0, + "value": "Add User" + } + ], + "auth.admin": [ + { + "type": 0, + "value": "Admin" + } + ], + "auth.create.account": [ + { + "type": 0, + "value": "Create Account" + } + ], + "auth.create.an.account": [ + { + "type": 0, + "value": "Create an account" + } + ], + "auth.create.an.account.intro": [ + { + "type": 0, + "value": "Welcome to Flood!" + } + ], + "auth.current.user": [ + { + "type": 0, + "value": "Current User" + } + ], + "auth.error.password.empty": [ + { + "type": 0, + "value": "Password cannot be empty." + } + ], + "auth.error.username.empty": [ + { + "type": 0, + "value": "Username cannot be empty." + } + ], + "auth.input.clear": [ + { + "type": 0, + "value": "Clear" + } + ], + "auth.log.in": [ + { + "type": 0, + "value": "Log In" + } + ], + "auth.login": [ + { + "type": 0, + "value": "Login" + } + ], + "auth.login.intro": [ + { + "type": 0, + "value": "Log in to your account." + } + ], + "auth.message.not.admin": [ + { + "type": 0, + "value": "User is not Admin" + } + ], + "auth.password": [ + { + "type": 0, + "value": "Password" + } + ], + "auth.user.accounts": [ + { + "type": 0, + "value": "User Accounts" + } + ], + "auth.username": [ + { + "type": 0, + "value": "Username" + } + ], + "button.add": [ + { + "type": 0, + "value": "Add" + } + ], + "button.cancel": [ + { + "type": 0, + "value": "Cancel" + } + ], + "button.close": [ + { + "type": 0, + "value": "Close" + } + ], + "button.download": [ + { + "type": 0, + "value": "Download" + } + ], + "button.new": [ + { + "type": 0, + "value": "New" + } + ], + "button.no": [ + { + "type": 0, + "value": "No" + } + ], + "button.ok": [ + { + "type": 0, + "value": "OK" + } + ], + "button.retry": [ + { + "type": 0, + "value": "Retry" + } + ], + "button.save": [ + { + "type": 0, + "value": "Save Settings" + } + ], + "button.save.feed": [ + { + "type": 0, + "value": "Save" + } + ], + "button.state.adding": [ + { + "type": 0, + "value": "Adding..." + } + ], + "button.yes": [ + { + "type": 0, + "value": "Yes" + } + ], + "connection-interruption.action.selection.config": [ + { + "type": 0, + "value": "Update client connection settings" + } + ], + "connection-interruption.action.selection.retry": [ + { + "type": 0, + "value": "Retry with current client connection settings" + } + ], + "connection-interruption.heading": [ + { + "type": 0, + "value": "Cannot connect to the client" + } + ], + "connection-interruption.not.admin": [ + { + "type": 0, + "value": "Please contact your Flood administrator if this continues." + } + ], + "connection-interruption.verification-error": [ + { + "type": 0, + "value": "Connection could not be verified." + } + ], + "connection.settings.client.select": [ + { + "type": 0, + "value": "Client" + } + ], + "connection.settings.error.empty": [ + { + "type": 0, + "value": "Connection settings can not be empty." + } + ], + "connection.settings.qbittorrent": [ + { + "type": 0, + "value": "qBittorrent" + } + ], + "connection.settings.qbittorrent.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.qbittorrent.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.qbittorrent.url.input.placeholder": [ + { + "type": 0, + "value": "URL to qBittorrent Web API" + } + ], + "connection.settings.qbittorrent.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.qbittorrent.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.rtorrent": [ + { + "type": 0, + "value": "rTorrent" + } + ], + "connection.settings.rtorrent.host": [ + { + "type": 0, + "value": "Host" + } + ], + "connection.settings.rtorrent.host.input.placeholder": [ + { + "type": 0, + "value": "Hostname or IP" + } + ], + "connection.settings.rtorrent.port": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.port.input.placeholder": [ + { + "type": 0, + "value": "Port" + } + ], + "connection.settings.rtorrent.socket": [ + { + "type": 0, + "value": "Path" + } + ], + "connection.settings.rtorrent.socket.input.placeholder": [ + { + "type": 0, + "value": "Path to socket" + } + ], + "connection.settings.rtorrent.type": [ + { + "type": 0, + "value": "Connection Type" + } + ], + "connection.settings.rtorrent.type.socket": [ + { + "type": 0, + "value": "Socket" + } + ], + "connection.settings.rtorrent.type.tcp": [ + { + "type": 0, + "value": "TCP" + } + ], + "connection.settings.rtorrent.type.tcp.warning": [ + { + "type": 0, + "value": "Exposing rTorrent via TCP may allow privilege escalation." + } + ], + "connection.settings.transmission": [ + { + "type": 0, + "value": "Transmission" + } + ], + "connection.settings.transmission.password": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.password.input.placeholder": [ + { + "type": 0, + "value": "Password" + } + ], + "connection.settings.transmission.url": [ + { + "type": 0, + "value": "URL" + } + ], + "connection.settings.transmission.url.input.placeholder": [ + { + "type": 0, + "value": "URL to Transmission RPC interface" + } + ], + "connection.settings.transmission.username": [ + { + "type": 0, + "value": "Username" + } + ], + "connection.settings.transmission.username.input.placeholder": [ + { + "type": 0, + "value": "Username" + } + ], + "connectivity.modal.content": [ + { + "type": 0, + "value": "Cannot connect to the client. Please update connection settings." + } + ], + "connectivity.modal.title": [ + { + "type": 0, + "value": "Connectivity Issue" + } + ], + "dependency.loading.notifications": [ + { + "type": 0, + "value": "Notifications" + } + ], + "dependency.loading.torrent.list": [ + { + "type": 0, + "value": "Torrent List" + } + ], + "dependency.loading.torrent.taxonomy": [ + { + "type": 0, + "value": "Torrent Taxonomy" + } + ], + "dependency.loading.transfer.history": [ + { + "type": 0, + "value": "Data Transfer History" + } + ], + "dependency.loading.transfer.rate.details": [ + { + "type": 0, + "value": "Data Transfer Rate Details" + } + ], + "feeds.add.automatic.download.rule": [ + { + "type": 0, + "value": "Add Download Rule" + } + ], + "feeds.add.feed": [ + { + "type": 0, + "value": "Add Feed" + } + ], + "feeds.applicable.feed": [ + { + "type": 0, + "value": "Applicable Feed" + } + ], + "feeds.apply.tags": [ + { + "type": 0, + "value": "Apply Tags" + } + ], + "feeds.browse.feeds": [ + { + "type": 0, + "value": "Browse feeds" + } + ], + "feeds.check": [ + { + "type": 0, + "value": "Validate the rule by trying it out. Not saved or sent." + } + ], + "feeds.exclude": [ + { + "type": 0, + "value": "Exclude" + } + ], + "feeds.exclude.pattern": [ + { + "type": 0, + "value": "Exclude Pattern" + } + ], + "feeds.existing.feeds": [ + { + "type": 0, + "value": "Existing Feeds" + } + ], + "feeds.existing.rules": [ + { + "type": 0, + "value": "Existing Rules" + } + ], + "feeds.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.label": [ + { + "type": 0, + "value": "Label" + } + ], + "feeds.match": [ + { + "type": 0, + "value": "Match" + } + ], + "feeds.match.count": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " match" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " matches" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "feeds.match.pattern": [ + { + "type": 0, + "value": "Match Pattern" + } + ], + "feeds.no.feeds.available": [ + { + "type": 0, + "value": "No feeds available." + } + ], + "feeds.no.feeds.defined": [ + { + "type": 0, + "value": "No feeds defined." + } + ], + "feeds.no.items.matching": [ + { + "type": 0, + "value": "No items matching search term." + } + ], + "feeds.no.rules.defined": [ + { + "type": 0, + "value": "No rules defined." + } + ], + "feeds.regEx": [ + { + "type": 0, + "value": "RegEx" + } + ], + "feeds.search": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.search.term": [ + { + "type": 0, + "value": "Search term" + } + ], + "feeds.select.feed": [ + { + "type": 0, + "value": "Select Feed" + } + ], + "feeds.select.interval": [ + { + "type": 0, + "value": "Interval" + } + ], + "feeds.start.on.load": [ + { + "type": 0, + "value": "Start on load" + } + ], + "feeds.tabs.download.rules": [ + { + "type": 0, + "value": "Download Rules" + } + ], + "feeds.tabs.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "feeds.tabs.heading": [ + { + "type": 0, + "value": "Torrent Feeds" + } + ], + "feeds.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "feeds.test.match": [ + { + "type": 0, + "value": "Test Match Pattern" + } + ], + "feeds.time.day": [ + { + "type": 0, + "value": "Days" + } + ], + "feeds.time.hr": [ + { + "type": 0, + "value": "Hours" + } + ], + "feeds.time.min": [ + { + "type": 0, + "value": "Minutes" + } + ], + "feeds.torrent.destination": [ + { + "type": 0, + "value": "Torrent Destination" + } + ], + "feeds.url": [ + { + "type": 0, + "value": "URL" + } + ], + "feeds.validation.interval.not.positive": [ + { + "type": 0, + "value": "The interval must be a positive integer." + } + ], + "feeds.validation.invalid.regular.expression": [ + { + "type": 0, + "value": "Invalid regular expression." + } + ], + "feeds.validation.must.select.feed": [ + { + "type": 0, + "value": "You must select a feed." + } + ], + "feeds.validation.must.specify.destination": [ + { + "type": 0, + "value": "You must specify a destination." + } + ], + "feeds.validation.must.specify.label": [ + { + "type": 0, + "value": "You must specify a label." + } + ], + "feeds.validation.must.specify.valid.feed.url": [ + { + "type": 0, + "value": "You must specify a valid feed URL." + } + ], + "filesystem.empty.directory": [ + { + "type": 0, + "value": "Empty directory." + } + ], + "filesystem.error.eacces": [ + { + "type": 0, + "value": "Flood does not have permission to read this directory." + } + ], + "filesystem.error.enoent": [ + { + "type": 0, + "value": "This path does not exist. It will be created." + } + ], + "filesystem.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred. Please try again." + } + ], + "filesystem.fetching": [ + { + "type": 0, + "value": "Fetching directory structure..." + } + ], + "filesystem.parent.directory": [ + { + "type": 0, + "value": "Parent Directory" + } + ], + "filter.all": [ + { + "type": 0, + "value": "All" + } + ], + "filter.status.active": [ + { + "type": 0, + "value": "Active" + } + ], + "filter.status.checking": [ + { + "type": 0, + "value": "Checking" + } + ], + "filter.status.completed": [ + { + "type": 0, + "value": "Complete" + } + ], + "filter.status.downloading": [ + { + "type": 0, + "value": "Downloading" + } + ], + "filter.status.error": [ + { + "type": 0, + "value": "Error" + } + ], + "filter.status.inactive": [ + { + "type": 0, + "value": "Inactive" + } + ], + "filter.status.seeding": [ + { + "type": 0, + "value": "Seeding" + } + ], + "filter.status.stopped": [ + { + "type": 0, + "value": "Stopped" + } + ], + "filter.status.title": [ + { + "type": 0, + "value": "Filter by Status" + } + ], + "filter.tag.title": [ + { + "type": 0, + "value": "Filter by Tag" + } + ], + "filter.tracker.title": [ + { + "type": 0, + "value": "Filter by Tracker" + } + ], + "filter.untagged": [ + { + "type": 0, + "value": "Untagged" + } + ], + "general.ago": [ + { + "type": 0, + "value": "ago" + } + ], + "general.at": [ + { + "type": 0, + "value": "at" + } + ], + "general.clipboard.copied": [ + { + "type": 0, + "value": "Copied" + } + ], + "general.clipboard.copy": [ + { + "type": 0, + "value": "Copy" + } + ], + "general.error.unknown": [ + { + "type": 0, + "value": "An unknown error occurred" + } + ], + "general.of": [ + { + "type": 0, + "value": "of" + } + ], + "general.to": [ + { + "type": 0, + "value": "to" + } + ], + "locale.language.auto": [ + { + "type": 0, + "value": "Automatic" + } + ], + "locale.language.translate": [ + { + "type": 0, + "value": "Start to translate" + } + ], + "mediainfo.execError": [ + { + "type": 0, + "value": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood." + } + ], + "mediainfo.fetching": [ + { + "type": 0, + "value": "Fetching..." + } + ], + "mediainfo.heading": [ + { + "type": 0, + "value": "Mediainfo Output" + } + ], + "notification.clear.all": [ + { + "type": 0, + "value": "Clear All" + } + ], + "notification.feed.torrent.added.body": [ + { + "type": 1, + "value": "title" + } + ], + "notification.feed.torrent.added.heading": [ + { + "type": 0, + "value": "Feed Item Queued" + } + ], + "notification.no.notification": [ + { + "type": 0, + "value": "No notification to display." + } + ], + "notification.showing": [ + { + "type": 0, + "value": "Showing" + } + ], + "notification.torrent.errored.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.errored.heading": [ + { + "type": 0, + "value": "Error Reported" + } + ], + "notification.torrent.finished.body": [ + { + "type": 1, + "value": "name" + } + ], + "notification.torrent.finished.heading": [ + { + "type": 0, + "value": "Finished Downloading" + } + ], + "priority.dont.download": [ + { + "type": 0, + "value": "Don't Download" + } + ], + "priority.high": [ + { + "type": 0, + "value": "High" + } + ], + "priority.low": [ + { + "type": 0, + "value": "Low" + } + ], + "priority.normal": [ + { + "type": 0, + "value": "Normal" + } + ], + "settings.about.flood": [ + { + "type": 0, + "value": "About Flood" + } + ], + "settings.bandwidth.slots.download.global.label": [ + { + "type": 0, + "value": "Download Slots Global" + } + ], + "settings.bandwidth.slots.download.label": [ + { + "type": 0, + "value": "Download Slots Per Torrent" + } + ], + "settings.bandwidth.slots.heading": [ + { + "type": 0, + "value": "Slot Availability" + } + ], + "settings.bandwidth.slots.upload.global.label": [ + { + "type": 0, + "value": "Upload Slots Global" + } + ], + "settings.bandwidth.slots.upload.label": [ + { + "type": 0, + "value": "Upload Slots Per Torrent" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.download.label": [ + { + "type": 0, + "value": "Dropdown Presets: Download" + } + ], + "settings.bandwidth.transferrate.dropdown.preset.upload.label": [ + { + "type": 0, + "value": "Dropdown Presets: Upload" + } + ], + "settings.bandwidth.transferrate.global.throttle.download": [ + { + "type": 0, + "value": "Global Download Rate Throttle" + } + ], + "settings.bandwidth.transferrate.global.throttle.upload": [ + { + "type": 0, + "value": "Global Upload Rate Throttle" + } + ], + "settings.bandwidth.transferrate.heading": [ + { + "type": 0, + "value": "Transfer Rate Throttles" + } + ], + "settings.connectivity.dht.label": [ + { + "type": 0, + "value": "Enable DHT" + } + ], + "settings.connectivity.dht.port.label": [ + { + "type": 0, + "value": "DHT Port" + } + ], + "settings.connectivity.dpd.heading": [ + { + "type": 0, + "value": "Decentralized Peer Discovery" + } + ], + "settings.connectivity.incoming.heading": [ + { + "type": 0, + "value": "Incoming Connections" + } + ], + "settings.connectivity.ip.hostname.label": [ + { + "type": 0, + "value": "Reported IP/Hostname" + } + ], + "settings.connectivity.max.http.connections": [ + { + "type": 0, + "value": "Maximum HTTP Connections" + } + ], + "settings.connectivity.peer.exchange.label": [ + { + "type": 0, + "value": "Enable Peer Exchange" + } + ], + "settings.connectivity.peers.desired.label": [ + { + "type": 0, + "value": "Peers Desired" + } + ], + "settings.connectivity.peers.heading": [ + { + "type": 0, + "value": "Peers" + } + ], + "settings.connectivity.peers.max.label": [ + { + "type": 0, + "value": "Maximum Peers" + } + ], + "settings.connectivity.peers.min.label": [ + { + "type": 0, + "value": "Minimum Peers" + } + ], + "settings.connectivity.peers.seeding.max.label": [ + { + "type": 0, + "value": "Maximum Peers Seeding" + } + ], + "settings.connectivity.peers.seeding.min.label": [ + { + "type": 0, + "value": "Minimum Peers Seeding" + } + ], + "settings.connectivity.port.open.label": [ + { + "type": 0, + "value": "Open Port" + } + ], + "settings.connectivity.port.randomize.label": [ + { + "type": 0, + "value": "Randomize Port" + } + ], + "settings.connectivity.port.range.label": [ + { + "type": 0, + "value": "Port Range" + } + ], + "settings.diskusage.mount.points": [ + { + "type": 0, + "value": "Disk Usage Mount Points" + } + ], + "settings.diskusage.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.resources.disk.check.hash.label": [ + { + "type": 0, + "value": "Verify Hash on Completion" + } + ], + "settings.resources.disk.download.location.label": [ + { + "type": 0, + "value": "Default Download Directory" + } + ], + "settings.resources.disk.heading": [ + { + "type": 0, + "value": "Disk" + } + ], + "settings.resources.max.open.files": [ + { + "type": 0, + "value": "Maximum Open Files" + } + ], + "settings.resources.memory.heading": [ + { + "type": 0, + "value": "Memory" + } + ], + "settings.resources.memory.max.label": [ + { + "type": 0, + "value": "Max Memory Usage" + } + ], + "settings.tabs.about": [ + { + "type": 0, + "value": "About" + } + ], + "settings.tabs.authentication": [ + { + "type": 0, + "value": "Authentication" + } + ], + "settings.tabs.bandwidth": [ + { + "type": 0, + "value": "Bandwidth" + } + ], + "settings.tabs.connectivity": [ + { + "type": 0, + "value": "Connectivity" + } + ], + "settings.tabs.diskusage": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "settings.tabs.heading": [ + { + "type": 0, + "value": "Settings" + } + ], + "settings.tabs.resources": [ + { + "type": 0, + "value": "Resources" + } + ], + "settings.tabs.userinterface": [ + { + "type": 0, + "value": "User Interface" + } + ], + "settings.ui.displayed.context.menu.items": [ + { + "type": 0, + "value": "Context Menu Items" + } + ], + "settings.ui.displayed.details": [ + { + "type": 0, + "value": "Torrent Detail Columns" + } + ], + "settings.ui.language": [ + { + "type": 0, + "value": "Language" + } + ], + "settings.ui.locale": [ + { + "type": 0, + "value": "Locale" + } + ], + "settings.ui.tag.selector.mode": [ + { + "type": 0, + "value": "Tag Selector Preference" + } + ], + "settings.ui.tag.selector.mode.multi": [ + { + "type": 0, + "value": "Multi Selection" + } + ], + "settings.ui.tag.selector.mode.single": [ + { + "type": 0, + "value": "Single Selection" + } + ], + "settings.ui.torrent.context.menu.items.show": [ + { + "type": 0, + "value": "Show" + } + ], + "settings.ui.torrent.details.enabled": [ + { + "type": 0, + "value": "Enabled" + } + ], + "settings.ui.torrent.details.tags.placement": [ + { + "type": 0, + "value": "In the expanded view, tags work best at the end of the list." + } + ], + "settings.ui.torrent.list": [ + { + "type": 0, + "value": "Torrent List Display" + } + ], + "settings.ui.torrent.size": [ + { + "type": 0, + "value": "Torrent Size" + } + ], + "settings.ui.torrent.size.condensed": [ + { + "type": 0, + "value": "Condensed View" + } + ], + "settings.ui.torrent.size.expanded": [ + { + "type": 0, + "value": "Expanded View" + } + ], + "sidebar.button.feeds": [ + { + "type": 0, + "value": "Feeds" + } + ], + "sidebar.button.log.out": [ + { + "type": 0, + "value": "Log Out" + } + ], + "sidebar.button.settings": [ + { + "type": 0, + "value": "Settings" + } + ], + "sidebar.button.speedlimits": [ + { + "type": 0, + "value": "Speed Limits" + } + ], + "sidebar.button.theme.dark": [ + { + "type": 0, + "value": "Dark Theme" + } + ], + "sidebar.button.theme.light": [ + { + "type": 0, + "value": "Light Theme" + } + ], + "sidebar.search.placeholder": [ + { + "type": 0, + "value": "Search torrents" + } + ], + "sidebar.speedlimits.download": [ + { + "type": 0, + "value": "DOWNLOAD" + } + ], + "sidebar.speedlimits.upload": [ + { + "type": 0, + "value": "UPLOAD" + } + ], + "sidebar.transferdata.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "sidebar.transferdata.uploaded": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "speed.unlimited": [ + { + "type": 0, + "value": "Unlimited" + } + ], + "status.diskusage.free": [ + { + "type": 0, + "value": "Free" + } + ], + "status.diskusage.title": [ + { + "type": 0, + "value": "Disk Usage" + } + ], + "status.diskusage.total": [ + { + "type": 0, + "value": "Total" + } + ], + "status.diskusage.used": [ + { + "type": 0, + "value": "Used" + } + ], + "torrent.list.peers": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "of" + }, + { + "type": 0, + "value": " " + }, + { + "type": 1, + "value": "total" + } + ], + "torrent.list.peers.of": [ + { + "type": 0, + "value": "of" + } + ], + "torrents.add.button.add": [ + { + "type": 0, + "value": "Add Torrent" + } + ], + "torrents.add.cookies.input.placeholder": [ + { + "type": 0, + "value": "Optional cookie-name=cookie-value" + } + ], + "torrents.add.cookies.label": [ + { + "type": 0, + "value": "Cookies" + } + ], + "torrents.add.destination.label": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.destination.placeholder": [ + { + "type": 0, + "value": "Destination" + } + ], + "torrents.add.heading": [ + { + "type": 0, + "value": "Add Torrents" + } + ], + "torrents.add.start.label": [ + { + "type": 0, + "value": "Start Torrent" + } + ], + "torrents.add.tab.create.title": [ + { + "type": 0, + "value": "Create" + } + ], + "torrents.add.tab.file.browse": [ + { + "type": 0, + "value": "or click to browse" + } + ], + "torrents.add.tab.file.drop": [ + { + "type": 0, + "value": "Drop some files here," + } + ], + "torrents.add.tab.file.title": [ + { + "type": 0, + "value": "By File" + } + ], + "torrents.add.tab.url.input.placeholder": [ + { + "type": 0, + "value": "Torrent URL or Magnet Link" + } + ], + "torrents.add.tab.url.register.magnet.handler": [ + { + "type": 0, + "value": "Register to handle magnet links" + } + ], + "torrents.add.tab.url.title": [ + { + "type": 0, + "value": "By URL" + } + ], + "torrents.add.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.add.torrents.label": [ + { + "type": 0, + "value": "Torrents" + } + ], + "torrents.create.base.name.input.placeholder": [ + { + "type": 0, + "value": "Optional base file or directory name of the torrent" + } + ], + "torrents.create.base.name.label": [ + { + "type": 0, + "value": "Base Name" + } + ], + "torrents.create.comment.input.placeholder": [ + { + "type": 0, + "value": "Optional comment in torrent file" + } + ], + "torrents.create.comment.label": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.create.info.source.input.placeholder": [ + { + "type": 0, + "value": "Optional source entry in infohash" + } + ], + "torrents.create.info.source.label": [ + { + "type": 0, + "value": "Info Source" + } + ], + "torrents.create.is.private.label": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.create.source.path.label": [ + { + "type": 0, + "value": "Source" + } + ], + "torrents.create.tags.input.placeholder": [ + { + "type": 0, + "value": "Tags in Flood. Not added to created torrent." + } + ], + "torrents.create.tracker.input.placeholder": [ + { + "type": 0, + "value": "Tracker URL" + } + ], + "torrents.create.trackers.label": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.destination.base_path": [ + { + "type": 0, + "value": "Use as Base Path" + } + ], + "torrents.destination.completed": [ + { + "type": 0, + "value": "Completed" + } + ], + "torrents.details.actions.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.details.actions.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.details.actions.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.details.details": [ + { + "type": 0, + "value": "Details" + } + ], + "torrents.details.files": [ + { + "type": 0, + "value": "Files" + } + ], + "torrents.details.files.download.file": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 0, + "value": "Download File" + } + ] + }, + "other": { + "value": [ + { + "type": 0, + "value": "Download Files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.files.loading": [ + { + "type": 0, + "value": "Loading file detail..." + } + ], + "torrents.details.general.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.details.general.connected": [ + { + "type": 1, + "value": "connected" + }, + { + "type": 0, + "value": " connected of " + }, + { + "type": 1, + "value": "total" + } + ], + "torrents.details.general.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.details.general.date.created": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.details.general.downloaded": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.details.general.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.details.general.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.details.general.heading.general": [ + { + "type": 0, + "value": "General" + } + ], + "torrents.details.general.heading.torrent": [ + { + "type": 0, + "value": "Torrent" + } + ], + "torrents.details.general.heading.tracker": [ + { + "type": 0, + "value": "Tracker" + } + ], + "torrents.details.general.heading.transfer": [ + { + "type": 0, + "value": "Transfer" + } + ], + "torrents.details.general.location": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.details.general.none": [ + { + "type": 0, + "value": "None" + } + ], + "torrents.details.general.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.general.scheduler": [ + { + "type": 0, + "value": "Scheduler" + } + ], + "torrents.details.general.scheduler.ignored": [ + { + "type": 0, + "value": "Ignored" + } + ], + "torrents.details.general.scheduler.obeyed": [ + { + "type": 0, + "value": "Obeyed" + } + ], + "torrents.details.general.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.details.general.size": [ + { + "type": 0, + "value": "Size" + } + ], + "torrents.details.general.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.details.general.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.details.general.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.details.general.type.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.details.general.type.public": [ + { + "type": 0, + "value": "Public" + } + ], + "torrents.details.mediainfo": [ + { + "type": 0, + "value": "Mediainfo" + } + ], + "torrents.details.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.details.peers.no.data": [ + { + "type": 0, + "value": "There is no peer data for this torrent." + } + ], + "torrents.details.selected.files": [ + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected file" + } + ] + }, + "other": { + "value": [ + { + "type": 1, + "value": "countElement" + }, + { + "type": 0, + "value": " selected files" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + } + ], + "torrents.details.selected.files.set.priority": [ + { + "type": 0, + "value": "Set Priority" + } + ], + "torrents.details.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.details.trackers.no.data": [ + { + "type": 0, + "value": "There is no tracker data for this torrent." + } + ], + "torrents.details.trackers.type": [ + { + "type": 0, + "value": "Type" + } + ], + "torrents.generate.magnet.heading": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.generate.magnet.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.generate.magnet.magnet": [ + { + "type": 0, + "value": "Magnet Link" + } + ], + "torrents.generate.magnet.magnet.with.trackers": [ + { + "type": 0, + "value": "Magnet Link with Trackers" + } + ], + "torrents.generate.magnet.private.torrent": [ + { + "type": 0, + "value": "This is a private torrent." + } + ], + "torrents.list.cannot.connect": [ + { + "type": 0, + "value": "Cannot connect to the client." + } + ], + "torrents.list.clear.filters": [ + { + "type": 0, + "value": "Clear Filters" + } + ], + "torrents.list.context.check.hash": [ + { + "type": 0, + "value": "Check Hash" + } + ], + "torrents.list.context.details": [ + { + "type": 0, + "value": "Torrent Details" + } + ], + "torrents.list.context.download": [ + { + "type": 0, + "value": "Download" + } + ], + "torrents.list.context.generate.magnet": [ + { + "type": 0, + "value": "Generate Magnet Link" + } + ], + "torrents.list.context.move": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.list.context.pause": [ + { + "type": 0, + "value": "Pause" + } + ], + "torrents.list.context.priority": [ + { + "type": 0, + "value": "Priority" + } + ], + "torrents.list.context.remove": [ + { + "type": 0, + "value": "Remove" + } + ], + "torrents.list.context.set.tags": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.list.context.set.trackers": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.list.context.start": [ + { + "type": 0, + "value": "Start" + } + ], + "torrents.list.context.stop": [ + { + "type": 0, + "value": "Stop" + } + ], + "torrents.list.drop": [ + { + "type": 0, + "value": "Drop files here to add them." + } + ], + "torrents.list.no.torrents": [ + { + "type": 0, + "value": "No torrents to display." + } + ], + "torrents.move.button.set.location": [ + { + "type": 0, + "value": "Set Location" + } + ], + "torrents.move.button.state.setting": [ + { + "type": 0, + "value": "Setting..." + } + ], + "torrents.move.check_hash.label": [ + { + "type": 0, + "value": "Check hash" + } + ], + "torrents.move.data.label": [ + { + "type": 0, + "value": "Move data" + } + ], + "torrents.move.heading": [ + { + "type": 0, + "value": "Set Torrent Location" + } + ], + "torrents.properties.comment": [ + { + "type": 0, + "value": "Comment" + } + ], + "torrents.properties.creation.date": [ + { + "type": 0, + "value": "Creation Date" + } + ], + "torrents.properties.date.added": [ + { + "type": 0, + "value": "Added" + } + ], + "torrents.properties.directory": [ + { + "type": 0, + "value": "Location" + } + ], + "torrents.properties.download.speed": [ + { + "type": 0, + "value": "Download Speed" + } + ], + "torrents.properties.download.total": [ + { + "type": 0, + "value": "Downloaded" + } + ], + "torrents.properties.eta": [ + { + "type": 0, + "value": "ETA" + } + ], + "torrents.properties.free.disk.space": [ + { + "type": 0, + "value": "Free Disk Space" + } + ], + "torrents.properties.hash": [ + { + "type": 0, + "value": "Hash" + } + ], + "torrents.properties.ignore.schedule": [ + { + "type": 0, + "value": "Ignore Scheduler" + } + ], + "torrents.properties.is.private": [ + { + "type": 0, + "value": "Private" + } + ], + "torrents.properties.name": [ + { + "type": 0, + "value": "Name" + } + ], + "torrents.properties.peers": [ + { + "type": 0, + "value": "Peers" + } + ], + "torrents.properties.percentage": [ + { + "type": 0, + "value": "Percent Complete" + } + ], + "torrents.properties.ratio": [ + { + "type": 0, + "value": "Ratio" + } + ], + "torrents.properties.seeds": [ + { + "type": 0, + "value": "Seeds" + } + ], + "torrents.properties.size": [ + { + "type": 0, + "value": "File Size" + } + ], + "torrents.properties.tags": [ + { + "type": 0, + "value": "Tags" + } + ], + "torrents.properties.tracker.message": [ + { + "type": 0, + "value": "Tracker Message" + } + ], + "torrents.properties.trackers": [ + { + "type": 0, + "value": "Trackers" + } + ], + "torrents.properties.upload.speed": [ + { + "type": 0, + "value": "Upload Speed" + } + ], + "torrents.properties.upload.total": [ + { + "type": 0, + "value": "Uploaded" + } + ], + "torrents.remove": [ + { + "type": 0, + "value": "Remove Torrents" + } + ], + "torrents.remove.are.you.sure": [ + { + "type": 0, + "value": "Are you sure you want to remove " + }, + { + "offset": 0, + "options": { + "=1": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrent" + } + ] + }, + "other": { + "value": [ + { + "type": 7 + }, + { + "type": 0, + "value": " torrents" + } + ] + } + }, + "pluralType": "cardinal", + "type": 6, + "value": "count" + }, + { + "type": 0, + "value": "?" + } + ], + "torrents.remove.delete.data": [ + { + "type": 0, + "value": "Delete data" + } + ], + "torrents.remove.error.no.torrents.selected": [ + { + "type": 0, + "value": "You haven't selected any torrents." + } + ], + "torrents.set.tags.button.set": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.tags.enter.tags": [ + { + "type": 0, + "value": "Enter tags" + } + ], + "torrents.set.tags.heading": [ + { + "type": 0, + "value": "Set Tags" + } + ], + "torrents.set.trackers.button.set": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.enter.tracker": [ + { + "type": 0, + "value": "Enter a tracker" + } + ], + "torrents.set.trackers.heading": [ + { + "type": 0, + "value": "Set Trackers" + } + ], + "torrents.set.trackers.loading.trackers": [ + { + "type": 0, + "value": "Loading trackers..." + } + ], + "torrents.sort.title": [ + { + "type": 0, + "value": "Sort By" + } + ], + "unit.size.byte": [ + { + "type": 0, + "value": "B" + } + ], + "unit.size.gigabyte": [ + { + "type": 0, + "value": "GB" + } + ], + "unit.size.kilobyte": [ + { + "type": 0, + "value": "kB" + } + ], + "unit.size.megabyte": [ + { + "type": 0, + "value": "MB" + } + ], + "unit.size.terabyte": [ + { + "type": 0, + "value": "TB" + } + ], + "unit.speed": [ + { + "type": 1, + "value": "baseUnit" + }, + { + "type": 0, + "value": "/s" + } + ], + "unit.time.day": [ + { + "type": 0, + "value": "d" + } + ], + "unit.time.hour": [ + { + "type": 0, + "value": "hr" + } + ], + "unit.time.infinity": [ + { + "type": 0, + "value": "∞" + } + ], + "unit.time.minute": [ + { + "type": 0, + "value": "m" + } + ], + "unit.time.second": [ + { + "type": 0, + "value": "s" + } + ], + "unit.time.week": [ + { + "type": 0, + "value": "wk" + } + ], + "unit.time.year": [ + { + "type": 0, + "value": "yr" + } + ] +} diff --git a/client/src/javascript/i18n/strings.json b/client/src/javascript/i18n/strings.json new file mode 100644 index 000000000..27901cc9e --- /dev/null +++ b/client/src/javascript/i18n/strings.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Add Torrent", + "actionbar.button.remove.torrent": "Remove Torrent", + "alert.torrent.add": "Successfully added {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Failed to add {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Successfully moved {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move.failed": "Failed to move {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Successfully removed {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Failed to remove {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Successfully saved settings.", + "auth.add.user": "Add User", + "auth.create.account": "Create Account", + "auth.create.an.account": "Create an account", + "auth.create.an.account.intro": "Welcome to Flood!", + "auth.current.user": "Current User", + "auth.error.username.empty": "Username cannot be empty.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "Log In", + "auth.login": "Login", + "auth.login.intro": "Log in to your account.", + "auth.password": "Password", + "auth.user.accounts": "User Accounts", + "auth.username": "Username", + "auth.admin": "Admin", + "auth.message.not.admin": "User is not Admin", + "button.add": "Add", + "button.cancel": "Cancel", + "button.close": "Close", + "button.download": "Download", + "button.no": "No", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Save Settings", + "button.save.feed": "Save", + "button.state.adding": "Adding...", + "button.yes": "Yes", + "button.new": "New", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Connection could not be verified.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Connectivity Issue", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "Add Download Rule", + "feeds.add.feed": "Add Feed", + "feeds.applicable.feed": "Applicable Feed", + "feeds.apply.tags": "Apply Tags", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Exclude Pattern", + "feeds.existing.feeds": "Existing Feeds", + "feeds.existing.rules": "Existing Rules", + "feeds.interval": "Interval", + "feeds.label": "Label", + "feeds.match.count": "{count, plural, =1 {# match} other {# matches}}", + "feeds.match.pattern": "Match Pattern", + "feeds.match": "Match", + "feeds.exclude": "Exclude", + "feeds.no.feeds.available": "No feeds available.", + "feeds.no.feeds.defined": "No feeds defined.", + "feeds.no.items.matching": "No items matching search term.", + "feeds.no.rules.defined": "No rules defined.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Select Feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start on load", + "feeds.tabs.download.rules": "Download Rules", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent Feeds", + "feeds.tags": "Tags", + "feeds.test.match": "Test Match Pattern", + "feeds.time.hr": "Hours", + "feeds.time.min": "Minutes", + "feeds.time.day": "Days", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Search term", + "feeds.search.term": "Search term", + "feeds.validation.invalid.regular.expression": "Invalid regular expression.", + "feeds.validation.must.select.feed": "You must select a feed.", + "feeds.validation.must.specify.destination": "You must specify a destination.", + "feeds.validation.must.specify.label": "You must specify a label.", + "feeds.validation.must.specify.valid.feed.url": "You must specify a valid feed URL.", + "feeds.validation.interval.not.positive": "The interval must be a positive integer.", + "feeds.browse.feeds": "Browse feeds", + "filesystem.empty.directory": "Empty directory.", + "filesystem.error.eacces": "Flood does not have permission to read this directory.", + "filesystem.error.enoent": "This path does not exist. It will be created.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "Fetching directory structure...", + "filesystem.parent.directory": "Parent Directory", + "filter.all": "All", + "filter.status.title": "Filter by Status", + "filter.status.downloading": "Downloading", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Complete", + "filter.status.active": "Active", + "filter.status.inactive": "Inactive", + "filter.status.error": "Error", + "filter.status.stopped": "Stopped", + "filter.status.checking": "Checking", + "filter.tracker.title": "Filter by Tracker", + "filter.tag.title": "Filter by Tag", + "filter.untagged": "Untagged", + "general.ago": "ago", + "general.at": "at", + "general.to": "to", + "general.of": "of", + "general.clipboard.copy": "Copy", + "general.clipboard.copied": "Copied", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.", + "mediainfo.fetching": "Fetching...", + "mediainfo.heading": "Mediainfo Output", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Finished Downloading", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Error Reported", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Clear All", + "notification.showing": "Showing", + "priority.dont.download": "Don't Download", + "priority.high": "High", + "priority.low": "Low", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Download Slots Global", + "settings.bandwidth.slots.download.label": "Download Slots Per Torrent", + "settings.bandwidth.slots.heading": "Slot Availability", + "settings.bandwidth.slots.upload.global.label": "Upload Slots Global", + "settings.bandwidth.slots.upload.label": "Upload Slots Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown Presets: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown Presets: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Global Download Rate Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Global Upload Rate Throttle", + "settings.bandwidth.transferrate.heading": "Transfer Rate Throttles", + "settings.connectivity.dht.label": "Enable DHT", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentralized Peer Discovery", + "settings.connectivity.incoming.heading": "Incoming Connections", + "settings.connectivity.ip.hostname.label": "Reported IP/Hostname", + "settings.connectivity.max.http.connections": "Maximum HTTP Connections", + "settings.connectivity.peer.exchange.label": "Enable Peer Exchange", + "settings.connectivity.peers.desired.label": "Peers Desired", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maximum Peers", + "settings.connectivity.peers.min.label": "Minimum Peers", + "settings.connectivity.peers.seeding.max.label": "Maximum Peers Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimum Peers Seeding", + "settings.connectivity.port.open.label": "Open Port", + "settings.connectivity.port.randomize.label": "Randomize Port", + "settings.connectivity.port.range.label": "Port Range", + "settings.resources.disk.check.hash.label": "Verify Hash on Completion", + "settings.resources.disk.download.location.label": "Default Download Directory", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maximum Open Files", + "settings.resources.memory.heading": "Memory", + "settings.resources.memory.max.label": "Max Memory Usage", + "settings.tabs.bandwidth": "Bandwidth", + "settings.tabs.connectivity": "Connectivity", + "settings.tabs.heading": "Settings", + "settings.tabs.resources": "Resources", + "settings.tabs.authentication": "Authentication", + "settings.tabs.userinterface": "User Interface", + "settings.tabs.diskusage": "Disk Usage", + "settings.tabs.about": "About", + "settings.ui.locale": "Locale", + "settings.ui.language": "Language", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent List Display", + "settings.ui.torrent.size": "Torrent Size", + "settings.ui.torrent.size.expanded": "Expanded View", + "settings.ui.torrent.size.condensed": "Condensed View", + "settings.ui.torrent.details.enabled": "Enabled", + "settings.ui.torrent.details.tags.placement": "In the expanded view, tags work best at the end of the list.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Show", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "About Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Settings", + "sidebar.button.speedlimits": "Speed Limits", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log Out", + "sidebar.search.placeholder": "Search torrents", + "sidebar.transferdata.downloaded": "Downloaded", + "sidebar.transferdata.uploaded": "Uploaded", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Unlimited", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "hr", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Add Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Destination", + "torrents.add.destination.placeholder": "Destination", + "torrents.add.heading": "Add Torrents", + "torrents.add.start.label": "Start Torrent", + "torrents.add.tab.file.browse": "or click to browse", + "torrents.add.tab.file.drop": "Drop some files here,", + "torrents.add.tab.file.title": "By File", + "torrents.add.tab.url.input.placeholder": "Torrent URL or Magnet Link", + "torrents.add.tab.url.title": "By URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "Use as Base Path", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Details", + "torrents.details.files": "Files", + "torrents.details.files.loading": "Loading file detail...", + "torrents.details.files.download.file": "{count, plural, =1 {Download File} other {Download Files}}", + "torrents.details.general.comment": "Comment", + "torrents.details.general.connected": "{connected} connected of {total}", + "torrents.details.general.date.added": "Added", + "torrents.details.general.date.created": "Creation Date", + "torrents.details.general.downloaded": "Downloaded", + "torrents.details.general.free.disk.space": "Free Disk Space", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "General", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfer", + "torrents.details.general.location": "Location", + "torrents.details.general.none": "None", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignored", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Scheduler", + "torrents.details.general.seeds": "Seeds", + "torrents.details.general.size": "Size", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Tracker Message", + "torrents.details.general.type.private": "Private", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "There is no peer data for this torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} selected file} other {{countElement} selected files}}", + "torrents.details.selected.files.set.priority": "Set Priority", + "torrents.details.trackers.no.data": "There is no tracker data for this torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Clear Filters", + "torrents.list.context.check.hash": "Check Hash", + "torrents.list.context.details": "Torrent Details", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Set Torrent Location", + "torrents.list.context.pause": "Pause", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "Priority", + "torrents.list.context.remove": "Remove", + "torrents.list.context.set.tags": "Set Tags", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stop", + "torrents.list.no.torrents": "No torrents to display.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "of", + "torrents.move.button.set.location": "Set Location", + "torrents.move.button.state.setting": "Setting...", + "torrents.move.data.label": "Move data", + "torrents.move.check_hash.label": "Check hash", + "torrents.move.heading": "Set Torrent Location", + "torrents.properties.date.added": "Added", + "torrents.properties.comment": "Comment", + "torrents.properties.creation.date": "Creation Date", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Download Speed", + "torrents.properties.download.total": "Downloaded", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Free Disk Space", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignore Scheduler", + "torrents.properties.is.private": "Private", + "torrents.properties.name": "Name", + "torrents.properties.percentage": "Percent Complete", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "File Size", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Tracker Message", + "torrents.properties.upload.speed": "Upload Speed", + "torrents.properties.upload.total": "Uploaded", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Are you sure you want to remove {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Delete data", + "torrents.remove.error.no.torrents.selected": "You haven't selected any torrents.", + "torrents.remove": "Remove Torrents", + "torrents.set.tags.button.set": "Set Tags", + "torrents.set.tags.heading": "Set Tags", + "torrents.set.tags.enter.tags": "Enter tags", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "Sort By", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Disk Usage", + "status.diskusage.used": "Used", + "status.diskusage.free": "Free", + "status.diskusage.total": "Total", + "locale.language.auto": "Automatic", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Details", + "dependency.loading.transfer.history": "Data Transfer History", + "dependency.loading.torrent.list": "Torrent List" +} diff --git a/client/src/javascript/i18n/translations/af.json b/client/src/javascript/i18n/translations/af.json new file mode 100644 index 000000000..27901cc9e --- /dev/null +++ b/client/src/javascript/i18n/translations/af.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Add Torrent", + "actionbar.button.remove.torrent": "Remove Torrent", + "alert.torrent.add": "Successfully added {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Failed to add {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Successfully moved {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move.failed": "Failed to move {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Successfully removed {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Failed to remove {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Successfully saved settings.", + "auth.add.user": "Add User", + "auth.create.account": "Create Account", + "auth.create.an.account": "Create an account", + "auth.create.an.account.intro": "Welcome to Flood!", + "auth.current.user": "Current User", + "auth.error.username.empty": "Username cannot be empty.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "Log In", + "auth.login": "Login", + "auth.login.intro": "Log in to your account.", + "auth.password": "Password", + "auth.user.accounts": "User Accounts", + "auth.username": "Username", + "auth.admin": "Admin", + "auth.message.not.admin": "User is not Admin", + "button.add": "Add", + "button.cancel": "Cancel", + "button.close": "Close", + "button.download": "Download", + "button.no": "No", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Save Settings", + "button.save.feed": "Save", + "button.state.adding": "Adding...", + "button.yes": "Yes", + "button.new": "New", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Connection could not be verified.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Connectivity Issue", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "Add Download Rule", + "feeds.add.feed": "Add Feed", + "feeds.applicable.feed": "Applicable Feed", + "feeds.apply.tags": "Apply Tags", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Exclude Pattern", + "feeds.existing.feeds": "Existing Feeds", + "feeds.existing.rules": "Existing Rules", + "feeds.interval": "Interval", + "feeds.label": "Label", + "feeds.match.count": "{count, plural, =1 {# match} other {# matches}}", + "feeds.match.pattern": "Match Pattern", + "feeds.match": "Match", + "feeds.exclude": "Exclude", + "feeds.no.feeds.available": "No feeds available.", + "feeds.no.feeds.defined": "No feeds defined.", + "feeds.no.items.matching": "No items matching search term.", + "feeds.no.rules.defined": "No rules defined.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Select Feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start on load", + "feeds.tabs.download.rules": "Download Rules", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent Feeds", + "feeds.tags": "Tags", + "feeds.test.match": "Test Match Pattern", + "feeds.time.hr": "Hours", + "feeds.time.min": "Minutes", + "feeds.time.day": "Days", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Search term", + "feeds.search.term": "Search term", + "feeds.validation.invalid.regular.expression": "Invalid regular expression.", + "feeds.validation.must.select.feed": "You must select a feed.", + "feeds.validation.must.specify.destination": "You must specify a destination.", + "feeds.validation.must.specify.label": "You must specify a label.", + "feeds.validation.must.specify.valid.feed.url": "You must specify a valid feed URL.", + "feeds.validation.interval.not.positive": "The interval must be a positive integer.", + "feeds.browse.feeds": "Browse feeds", + "filesystem.empty.directory": "Empty directory.", + "filesystem.error.eacces": "Flood does not have permission to read this directory.", + "filesystem.error.enoent": "This path does not exist. It will be created.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "Fetching directory structure...", + "filesystem.parent.directory": "Parent Directory", + "filter.all": "All", + "filter.status.title": "Filter by Status", + "filter.status.downloading": "Downloading", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Complete", + "filter.status.active": "Active", + "filter.status.inactive": "Inactive", + "filter.status.error": "Error", + "filter.status.stopped": "Stopped", + "filter.status.checking": "Checking", + "filter.tracker.title": "Filter by Tracker", + "filter.tag.title": "Filter by Tag", + "filter.untagged": "Untagged", + "general.ago": "ago", + "general.at": "at", + "general.to": "to", + "general.of": "of", + "general.clipboard.copy": "Copy", + "general.clipboard.copied": "Copied", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.", + "mediainfo.fetching": "Fetching...", + "mediainfo.heading": "Mediainfo Output", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Finished Downloading", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Error Reported", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Clear All", + "notification.showing": "Showing", + "priority.dont.download": "Don't Download", + "priority.high": "High", + "priority.low": "Low", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Download Slots Global", + "settings.bandwidth.slots.download.label": "Download Slots Per Torrent", + "settings.bandwidth.slots.heading": "Slot Availability", + "settings.bandwidth.slots.upload.global.label": "Upload Slots Global", + "settings.bandwidth.slots.upload.label": "Upload Slots Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown Presets: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown Presets: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Global Download Rate Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Global Upload Rate Throttle", + "settings.bandwidth.transferrate.heading": "Transfer Rate Throttles", + "settings.connectivity.dht.label": "Enable DHT", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentralized Peer Discovery", + "settings.connectivity.incoming.heading": "Incoming Connections", + "settings.connectivity.ip.hostname.label": "Reported IP/Hostname", + "settings.connectivity.max.http.connections": "Maximum HTTP Connections", + "settings.connectivity.peer.exchange.label": "Enable Peer Exchange", + "settings.connectivity.peers.desired.label": "Peers Desired", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maximum Peers", + "settings.connectivity.peers.min.label": "Minimum Peers", + "settings.connectivity.peers.seeding.max.label": "Maximum Peers Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimum Peers Seeding", + "settings.connectivity.port.open.label": "Open Port", + "settings.connectivity.port.randomize.label": "Randomize Port", + "settings.connectivity.port.range.label": "Port Range", + "settings.resources.disk.check.hash.label": "Verify Hash on Completion", + "settings.resources.disk.download.location.label": "Default Download Directory", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maximum Open Files", + "settings.resources.memory.heading": "Memory", + "settings.resources.memory.max.label": "Max Memory Usage", + "settings.tabs.bandwidth": "Bandwidth", + "settings.tabs.connectivity": "Connectivity", + "settings.tabs.heading": "Settings", + "settings.tabs.resources": "Resources", + "settings.tabs.authentication": "Authentication", + "settings.tabs.userinterface": "User Interface", + "settings.tabs.diskusage": "Disk Usage", + "settings.tabs.about": "About", + "settings.ui.locale": "Locale", + "settings.ui.language": "Language", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent List Display", + "settings.ui.torrent.size": "Torrent Size", + "settings.ui.torrent.size.expanded": "Expanded View", + "settings.ui.torrent.size.condensed": "Condensed View", + "settings.ui.torrent.details.enabled": "Enabled", + "settings.ui.torrent.details.tags.placement": "In the expanded view, tags work best at the end of the list.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Show", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "About Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Settings", + "sidebar.button.speedlimits": "Speed Limits", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log Out", + "sidebar.search.placeholder": "Search torrents", + "sidebar.transferdata.downloaded": "Downloaded", + "sidebar.transferdata.uploaded": "Uploaded", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Unlimited", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "hr", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Add Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Destination", + "torrents.add.destination.placeholder": "Destination", + "torrents.add.heading": "Add Torrents", + "torrents.add.start.label": "Start Torrent", + "torrents.add.tab.file.browse": "or click to browse", + "torrents.add.tab.file.drop": "Drop some files here,", + "torrents.add.tab.file.title": "By File", + "torrents.add.tab.url.input.placeholder": "Torrent URL or Magnet Link", + "torrents.add.tab.url.title": "By URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "Use as Base Path", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Details", + "torrents.details.files": "Files", + "torrents.details.files.loading": "Loading file detail...", + "torrents.details.files.download.file": "{count, plural, =1 {Download File} other {Download Files}}", + "torrents.details.general.comment": "Comment", + "torrents.details.general.connected": "{connected} connected of {total}", + "torrents.details.general.date.added": "Added", + "torrents.details.general.date.created": "Creation Date", + "torrents.details.general.downloaded": "Downloaded", + "torrents.details.general.free.disk.space": "Free Disk Space", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "General", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfer", + "torrents.details.general.location": "Location", + "torrents.details.general.none": "None", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignored", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Scheduler", + "torrents.details.general.seeds": "Seeds", + "torrents.details.general.size": "Size", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Tracker Message", + "torrents.details.general.type.private": "Private", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "There is no peer data for this torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} selected file} other {{countElement} selected files}}", + "torrents.details.selected.files.set.priority": "Set Priority", + "torrents.details.trackers.no.data": "There is no tracker data for this torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Clear Filters", + "torrents.list.context.check.hash": "Check Hash", + "torrents.list.context.details": "Torrent Details", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Set Torrent Location", + "torrents.list.context.pause": "Pause", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "Priority", + "torrents.list.context.remove": "Remove", + "torrents.list.context.set.tags": "Set Tags", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stop", + "torrents.list.no.torrents": "No torrents to display.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "of", + "torrents.move.button.set.location": "Set Location", + "torrents.move.button.state.setting": "Setting...", + "torrents.move.data.label": "Move data", + "torrents.move.check_hash.label": "Check hash", + "torrents.move.heading": "Set Torrent Location", + "torrents.properties.date.added": "Added", + "torrents.properties.comment": "Comment", + "torrents.properties.creation.date": "Creation Date", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Download Speed", + "torrents.properties.download.total": "Downloaded", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Free Disk Space", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignore Scheduler", + "torrents.properties.is.private": "Private", + "torrents.properties.name": "Name", + "torrents.properties.percentage": "Percent Complete", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "File Size", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Tracker Message", + "torrents.properties.upload.speed": "Upload Speed", + "torrents.properties.upload.total": "Uploaded", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Are you sure you want to remove {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Delete data", + "torrents.remove.error.no.torrents.selected": "You haven't selected any torrents.", + "torrents.remove": "Remove Torrents", + "torrents.set.tags.button.set": "Set Tags", + "torrents.set.tags.heading": "Set Tags", + "torrents.set.tags.enter.tags": "Enter tags", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "Sort By", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Disk Usage", + "status.diskusage.used": "Used", + "status.diskusage.free": "Free", + "status.diskusage.total": "Total", + "locale.language.auto": "Automatic", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Details", + "dependency.loading.transfer.history": "Data Transfer History", + "dependency.loading.torrent.list": "Torrent List" +} diff --git a/client/src/javascript/i18n/translations/ar.json b/client/src/javascript/i18n/translations/ar.json new file mode 100644 index 000000000..c35a2edd9 --- /dev/null +++ b/client/src/javascript/i18n/translations/ar.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "ابدأ التورينت", + "actionbar.button.stop.torrent": "إيقاف التورنت", + "actionbar.button.add.torrent": "إضافة تورنت", + "actionbar.button.remove.torrent": "إزالة التورنت", + "alert.torrent.add": "تمت إضافة {countElement} {count, plural, =1 {تورنت} other {تورنت}}", + "alert.torrent.add.failed": "فشل في إضافة {countElement} {count, plural, =1 {تورنت} other {تورنت}}", + "alert.torrent.move": "تم نقل {countElement} بنجاح {count, plural, =1 {تورنت} other {تورنت}}", + "alert.torrent.move.failed": "فشل نقل {countElement} {count, plural, =1 {تورنت} other {تورنت}}", + "alert.torrent.remove": "تم بنجاح إزالة {countElement} {count, plural, =1 {تورنت} other {تورنت}}", + "alert.torrent.remove.failed": "فشل في إزالة {countElement} {count, plural, =1 {تورنت} other {تورنت}}", + "alert.settings.saved": "تم حفظ الإعدادات بنجاح.", + "auth.add.user": "إضافة مستخدم", + "auth.create.account": "إنشاء حساب", + "auth.create.an.account": "إنشاء حساب", + "auth.create.an.account.intro": "مرحبا بكم في الفيضان!", + "auth.current.user": "المستخدم الحالي", + "auth.error.username.empty": "اسم المستخدم لا يمكن أن يكون فارغاً.", + "auth.error.password.empty": "كلمة المرور لا يمكن أن تكون فارغة.", + "auth.input.clear": "Clear", + "auth.log.in": "تسجيل الدخول", + "auth.login": "تسجيل الدخول", + "auth.login.intro": "تسجيل الدخول إلى حسابك.", + "auth.password": "كلمة المرور", + "auth.user.accounts": "حسابات المستخدم", + "auth.username": "اسم المستخدم", + "auth.admin": "المشرف", + "auth.message.not.admin": "المستخدم ليس مشرف", + "button.add": "إضافة", + "button.cancel": "إلغاء", + "button.close": "Close", + "button.download": "تنزيل", + "button.no": "لا", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "حفظ الإعدادات", + "button.save.feed": "حفظ", + "button.state.adding": "إضافة...", + "button.yes": "نعم", + "button.new": "جديد", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "تعذر التحقق من الاتصال.", + "connection.settings.client.select": "العميل", + "connection.settings.error.empty": "لا يمكن أن تكون إعدادات الاتصال فارغة.", + "connection.settings.rtorrent": "تورنت", + "connection.settings.rtorrent.type": "نوع الاتصال", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "المضيف", + "connection.settings.rtorrent.host.input.placeholder": "اسم المضيف أو IP", + "connection.settings.rtorrent.port": "المنفذ", + "connection.settings.rtorrent.port.input.placeholder": "المنفذ", + "connection.settings.rtorrent.socket": "المسار", + "connection.settings.rtorrent.socket.input.placeholder": "المسار إلى المقبس", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "الرابط", + "connection.settings.qbittorrent.url.input.placeholder": "عنوان URL ل qBittorrent Web API", + "connection.settings.qbittorrent.username": "اسم المستخدم", + "connection.settings.qbittorrent.username.input.placeholder": "اسم المستخدم", + "connection.settings.qbittorrent.password": "كلمة المرور", + "connection.settings.qbittorrent.password.input.placeholder": "كلمة المرور", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "مشكلة في الاتصال", + "connectivity.modal.content": "لا يمكن الاتصال بالعميل. الرجاء تحديث إعدادات الاتصال.", + "feeds.add.automatic.download.rule": "إضافة قاعدة تحميل", + "feeds.add.feed": "إضافة موجز ويب", + "feeds.applicable.feed": "تغذية قابلة للتطبيق", + "feeds.apply.tags": "تطبيق العلامات", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "استبعاد النمط", + "feeds.existing.feeds": "التحديثات الموجودة", + "feeds.existing.rules": "القواعد الحالية", + "feeds.interval": "الفاصل", + "feeds.label": "تسمية", + "feeds.match.count": "{count, plural, =1 {# تطابق} other {# يتطابق}}", + "feeds.match.pattern": "نمط المطابقة", + "feeds.match": "المباراة", + "feeds.exclude": "استبعاد", + "feeds.no.feeds.available": "لا توجد تغذية متاحة.", + "feeds.no.feeds.defined": "لم يتم تعريف أي تغذية.", + "feeds.no.items.matching": "لا توجد عناصر مطابقة لمصطلح البحث.", + "feeds.no.rules.defined": "لا توجد قواعد معرفة.", + "feeds.regEx": "ريريكس", + "feeds.select.feed": "حدد موجز الويب", + "feeds.select.interval": "الفاصل", + "feeds.start.on.load": "البدء عند التحميل", + "feeds.tabs.download.rules": "قواعد التحميل", + "feeds.tabs.feeds": "التحديثات", + "feeds.tabs.heading": "تغذية تورنت", + "feeds.tags": "الوسوم", + "feeds.test.match": "اختبار نمط المطابقة", + "feeds.time.hr": "الساعات", + "feeds.time.min": "دقائق", + "feeds.time.day": "أيام", + "feeds.torrent.destination": "وجهة تورنت", + "feeds.url": "الرابط", + "feeds.search": "مصطلح البحث", + "feeds.search.term": "مصطلح البحث", + "feeds.validation.invalid.regular.expression": "تعبير عادي غير صالح.", + "feeds.validation.must.select.feed": "يجب عليك تحديد التغذية.", + "feeds.validation.must.specify.destination": "يجب عليك تحديد الوجهة.", + "feeds.validation.must.specify.label": "يجب عليك تحديد التسمية.", + "feeds.validation.must.specify.valid.feed.url": "يجب عليك تحديد رابط تغذية صالح.", + "feeds.validation.interval.not.positive": "الفاصل الزمني يجب أن يكون عددا صحيحا إيجابيا.", + "feeds.browse.feeds": "تصفح الخلاصات", + "filesystem.empty.directory": "مجلد فارغ.", + "filesystem.error.eacces": "الفيضان ليس لديه الصلاحية لقراءة هذا الدليل.", + "filesystem.error.enoent": "هذا المسار غير موجود. سيتم إنشاؤه.", + "filesystem.error.unknown": "حدث خطأ غير معروف. الرجاء المحاولة مرة أخرى.", + "filesystem.fetching": "جلب بنية الدليل...", + "filesystem.parent.directory": "الدليل الأصلي", + "filter.all": "الكل", + "filter.status.title": "تصفية حسب الحالة", + "filter.status.downloading": "تنزيل", + "filter.status.seeding": "البذور", + "filter.status.completed": "Complete", + "filter.status.active": "نشط", + "filter.status.inactive": "غير نشط", + "filter.status.error": "خطأ", + "filter.status.stopped": "توقفت", + "filter.status.checking": "التحقق", + "filter.tracker.title": "تصفية حسب التتبع", + "filter.tag.title": "تصفية حسب العلامة", + "filter.untagged": "غير موسومة", + "general.ago": "مضت", + "general.at": "في", + "general.to": "إلى", + "general.of": "من", + "general.clipboard.copy": "نسخ", + "general.clipboard.copied": "منسوخ", + "general.error.unknown": "حدث خطأ غير معروف", + "mediainfo.execError": "حدث خطأ أثناء تشغيل mediainfo على الخادم. تحقق من أن mediainfo مثبت ومتاح في PATH للفيضان.", + "mediainfo.fetching": "إحضار ...", + "mediainfo.heading": "ناتج ميداينفو", + "notification.feed.torrent.added.heading": "قائمة انتظار عنصر التغذية", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "انتهى التحميل", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "تم الإبلاغ عن خطأ", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "مسح الكل", + "notification.showing": "عرض", + "priority.dont.download": "عدم التحميل", + "priority.high": "مرتفع", + "priority.low": "منخفض", + "priority.normal": "عادي", + "settings.bandwidth.slots.download.global.label": "تحميل الفتحات العالمية", + "settings.bandwidth.slots.download.label": "تنزيل الفتحات لكل تورينت", + "settings.bandwidth.slots.heading": "توفر الخانة", + "settings.bandwidth.slots.upload.global.label": "تحميل خانات عامة", + "settings.bandwidth.slots.upload.label": "تحميل الفتحات لكل تورينت", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "المنسدلة مسبقاً: تحميل", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "القائمة المنسدلة مسبقاً: تحميل", + "settings.bandwidth.transferrate.global.throttle.download": "خنق معدل التحميل العالمي", + "settings.bandwidth.transferrate.global.throttle.upload": "خنق معدل التحميل العالمي", + "settings.bandwidth.transferrate.heading": "خانات معدل التحويل", + "settings.connectivity.dht.label": "تمكين DHT", + "settings.connectivity.dht.port.label": "منفذ DHT", + "settings.connectivity.dpd.heading": "اكتشاف الند اللامركزي", + "settings.connectivity.incoming.heading": "اتصالات واردة", + "settings.connectivity.ip.hostname.label": "اسم IP/المضيف المبلغ عنه", + "settings.connectivity.max.http.connections": "الحد الأقصى لاتصالات HTTP", + "settings.connectivity.peer.exchange.label": "تمكين تبادل الأقران", + "settings.connectivity.peers.desired.label": "الند المنشود", + "settings.connectivity.peers.heading": "أقران", + "settings.connectivity.peers.max.label": "الحد الأقصى للنظراء", + "settings.connectivity.peers.min.label": "الحد الأدنى للنظراء", + "settings.connectivity.peers.seeding.max.label": "الحد الأقصى لبذور النظراء", + "settings.connectivity.peers.seeding.min.label": "الحد الأدنى لبذور الند", + "settings.connectivity.port.open.label": "فتح المنفذ", + "settings.connectivity.port.randomize.label": "تصوير المنفذ عشوائي", + "settings.connectivity.port.range.label": "نطاق المنفذ", + "settings.resources.disk.check.hash.label": "التحقق من التجزئة عند الاكتمال", + "settings.resources.disk.download.location.label": "دليل التحميل الافتراضي", + "settings.resources.disk.heading": "قرص", + "settings.resources.max.open.files": "الحد الأقصى للملفات المفتوحة", + "settings.resources.memory.heading": "الذاكرة", + "settings.resources.memory.max.label": "الحد الأقصى لاستخدام الذاكرة", + "settings.tabs.bandwidth": "عرض التردد", + "settings.tabs.connectivity": "الاتصال", + "settings.tabs.heading": "الإعدادات", + "settings.tabs.resources": "الموارد", + "settings.tabs.authentication": "المصادقة", + "settings.tabs.userinterface": "واجهة المستخدم", + "settings.tabs.diskusage": "استخدام القرص", + "settings.tabs.about": "حول", + "settings.ui.locale": "محلي", + "settings.ui.language": "اللغة", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "عرض قائمة التورينت", + "settings.ui.torrent.size": "حجم التورنت", + "settings.ui.torrent.size.expanded": "عرض موسع", + "settings.ui.torrent.size.condensed": "عرض مكثف", + "settings.ui.torrent.details.enabled": "تمكين", + "settings.ui.torrent.details.tags.placement": "ومن وجهة النظر الموسعة، فإن العلامات تعمل على أفضل وجه في نهاية القائمة.", + "settings.ui.torrent.context.menu.items.show": "إظهار", + "settings.ui.displayed.details": "أعمدة تفاصيل تورنت", + "settings.ui.displayed.context.menu.items": "عناصر قائمة السياق", + "settings.diskusage.show": "إظهار", + "settings.diskusage.mount.points": "نقاط استخدام القرص", + "settings.about.flood": "حول الفيضان", + "sidebar.button.feeds": "التحديثات", + "sidebar.button.settings": "الإعدادات", + "sidebar.button.speedlimits": "حدود السرعة", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "تسجيل الخروج", + "sidebar.search.placeholder": "البحث عن التورنت", + "sidebar.transferdata.downloaded": "تم التحميل", + "sidebar.transferdata.uploaded": "تم الرفع", + "sidebar.speedlimits.download": "تحميل", + "sidebar.speedlimits.upload": "تحميل", + "speed.unlimited": "غير محدود", + "unit.size.byte": "ب", + "unit.size.kilobyte": "كيلوبايت", + "unit.size.megabyte": "ميغابايت", + "unit.size.gigabyte": "جيجابايت", + "unit.size.terabyte": "تيلبايت", + "unit.speed": "{baseUnit}/ث", + "unit.time.year": "yr", + "unit.time.week": "تأجج", + "unit.time.day": "د", + "unit.time.hour": "ساعة", + "unit.time.minute": "د", + "unit.time.second": "ث", + "unit.time.infinity": "∞", + "torrents.add.button.add": "إضافة تورنت", + "torrents.add.cookies.label": "الكعكات", + "torrents.add.cookies.input.placeholder": "قيمة cookie-name=cookie-value", + "torrents.add.destination.label": "الوجهة", + "torrents.add.destination.placeholder": "الوجهة", + "torrents.add.heading": "إضافة تورينت", + "torrents.add.start.label": "ابدأ التورينت", + "torrents.add.tab.file.browse": "أو انقر لتصفح", + "torrents.add.tab.file.drop": "إسقاط بعض الملفات هنا،", + "torrents.add.tab.file.title": "حسب الملف", + "torrents.add.tab.url.input.placeholder": "رابط تورينت أو رابط الـ Magnet", + "torrents.add.tab.url.title": "حسب عنوان URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "إنشاء", + "torrents.add.torrents.label": "تورينتس", + "torrents.add.tags": "الوسوم", + "torrents.create.source.path.label": "المصدر", + "torrents.create.trackers.label": "المتتبعون", + "torrents.create.tracker.input.placeholder": "رابط التتبع", + "torrents.create.base.name.label": "اسم القاعدة", + "torrents.create.base.name.input.placeholder": "اسم ملف أساسي أو دليل تورنت اختياري", + "torrents.create.comment.label": "تعليق", + "torrents.create.comment.input.placeholder": "التعليق الاختياري في ملف تورنت", + "torrents.create.info.source.label": "مصدر المعلومات", + "torrents.create.info.source.input.placeholder": "إدخال المصدر الاختياري في Infohash", + "torrents.create.is.private.label": "خاص", + "torrents.create.tags.input.placeholder": "العلامات في الفيضان. غير مضافة إلى التورنت التي تم إنشاؤها.", + "torrents.destination.base_path": "استخدام كمسار أساسي", + "torrents.destination.completed": "مكتمل", + "torrents.details.actions.pause": "إيقاف", + "torrents.details.actions.start": "ابدأ", + "torrents.details.actions.stop": "توقف", + "torrents.details.details": "التفاصيل", + "torrents.details.files": "الملفات", + "torrents.details.files.loading": "تحميل تفاصيل الملف...", + "torrents.details.files.download.file": "{count, plural, =1 {تحميل الملف} other {تحميل الملفات}}", + "torrents.details.general.comment": "تعليق", + "torrents.details.general.connected": "{connected} متصل بـ {total}", + "torrents.details.general.date.added": "أضيف", + "torrents.details.general.date.created": "تاريخ الإنشاء", + "torrents.details.general.downloaded": "تم التحميل", + "torrents.details.general.free.disk.space": "مساحة القرص الحرة", + "torrents.details.general.hash": "التجزئة", + "torrents.details.general.heading.general": "عام", + "torrents.details.general.heading.torrent": "تورنت", + "torrents.details.general.heading.tracker": "المتتبع", + "torrents.details.general.heading.transfer": "نقل", + "torrents.details.general.location": "الموقع", + "torrents.details.general.none": "لا", + "torrents.details.general.peers": "أقران", + "torrents.details.general.scheduler.ignored": "تجاهل", + "torrents.details.general.scheduler.obeyed": "عقيب", + "torrents.details.general.scheduler": "جدولة", + "torrents.details.general.seeds": "بذور", + "torrents.details.general.size": "الحجم", + "torrents.details.general.tags": "الوسوم", + "torrents.details.general.tracker.message": "رسالة تتبع", + "torrents.details.general.type.private": "خاص", + "torrents.details.general.type.public": "عامة", + "torrents.details.general.type": "نوع", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "لا توجد بيانات أقران لهذا التورنت.", + "torrents.details.peers": "أقران", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} ملف محدد} other {{countElement} ملفات محددة}}", + "torrents.details.selected.files.set.priority": "تعيين الأولوية", + "torrents.details.trackers.no.data": "لا توجد بيانات تتبع لهذا التورنت.", + "torrents.details.trackers.type": "نوع", + "torrents.details.trackers": "المتتبعون", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "مسح الفلاتر", + "torrents.list.context.check.hash": "تحقق من التجزئة", + "torrents.list.context.details": "تفاصيل التورنت", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "تعيين موقع التورنت", + "torrents.list.context.pause": "إيقاف", + "torrents.list.context.download": "تنزيل", + "torrents.list.context.priority": "الأولوية", + "torrents.list.context.remove": "إزالة", + "torrents.list.context.set.tags": "تعيين العلامات", + "torrents.list.context.set.trackers": "تعيين التتبع", + "torrents.list.context.start": "ابدأ", + "torrents.list.context.stop": "توقف", + "torrents.list.no.torrents": "لا يوجد سلالات لعرضها.", + "torrents.list.drop": "إسقاط الملفات هنا لإضافتها.", + "torrents.list.cannot.connect": "لا يمكن الاتصال بالعميل.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "من", + "torrents.move.button.set.location": "تعيين الموقع", + "torrents.move.button.state.setting": "إعداد...", + "torrents.move.data.label": "نقل البيانات", + "torrents.move.check_hash.label": "تحقق من التجزئة", + "torrents.move.heading": "تعيين موقع التورنت", + "torrents.properties.date.added": "أضيف", + "torrents.properties.comment": "تعليق", + "torrents.properties.creation.date": "تاريخ الإنشاء", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "سرعة التنزيل", + "torrents.properties.download.total": "تم التحميل", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "مساحة القرص الحرة", + "torrents.properties.hash": "التجزئة", + "torrents.properties.ignore.schedule": "تجاهل المجدول", + "torrents.properties.is.private": "خاص", + "torrents.properties.name": "الاسم", + "torrents.properties.percentage": "اكتمل النسبة", + "torrents.properties.ratio": "النسبة", + "torrents.properties.size": "حجم الملف", + "torrents.properties.tags": "الوسوم", + "torrents.properties.tracker.message": "رسالة تتبع", + "torrents.properties.upload.speed": "سرعة التحميل", + "torrents.properties.upload.total": "تم الرفع", + "torrents.properties.seeds": "بذور", + "torrents.properties.peers": "أقران", + "torrents.properties.trackers": "المتتبعون", + "torrents.remove.are.you.sure": "هل أنت متأكد من أنك تريد إزالة {count, plural, =1 {# تورنت} other {# تورنت}}?", + "torrents.remove.delete.data": "حذف البيانات", + "torrents.remove.error.no.torrents.selected": "لم تقم باختيار أي تورنات.", + "torrents.remove": "إزالة التورنت", + "torrents.set.tags.button.set": "تعيين العلامات", + "torrents.set.tags.heading": "تعيين العلامات", + "torrents.set.tags.enter.tags": "أدخل العلامات", + "torrents.set.trackers.button.set": "تعيين التتبع", + "torrents.set.trackers.heading": "تعيين التتبع", + "torrents.set.trackers.enter.tracker": "أدخل متتبع", + "torrents.set.trackers.loading.trackers": "جاري تحميل التتبع...", + "torrents.sort.title": "الترتيب حسب", + "connection-interruption.heading": "لا يمكن الاتصال بالعميل", + "status.diskusage.title": "استخدام القرص", + "status.diskusage.used": "مستخدم", + "status.diskusage.free": "مجاني", + "status.diskusage.total": "المجموع", + "locale.language.auto": "تلقائي", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "الإشعارات", + "dependency.loading.torrent.taxonomy": "تصنيف تورنت", + "dependency.loading.transfer.rate.details": "تفاصيل معدل نقل البيانات", + "dependency.loading.transfer.history": "سجل نقل البيانات", + "dependency.loading.torrent.list": "قائمة التورينت" +} diff --git a/client/src/javascript/i18n/translations/ca.json b/client/src/javascript/i18n/translations/ca.json new file mode 100644 index 000000000..27901cc9e --- /dev/null +++ b/client/src/javascript/i18n/translations/ca.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Add Torrent", + "actionbar.button.remove.torrent": "Remove Torrent", + "alert.torrent.add": "Successfully added {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Failed to add {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Successfully moved {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move.failed": "Failed to move {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Successfully removed {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Failed to remove {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Successfully saved settings.", + "auth.add.user": "Add User", + "auth.create.account": "Create Account", + "auth.create.an.account": "Create an account", + "auth.create.an.account.intro": "Welcome to Flood!", + "auth.current.user": "Current User", + "auth.error.username.empty": "Username cannot be empty.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "Log In", + "auth.login": "Login", + "auth.login.intro": "Log in to your account.", + "auth.password": "Password", + "auth.user.accounts": "User Accounts", + "auth.username": "Username", + "auth.admin": "Admin", + "auth.message.not.admin": "User is not Admin", + "button.add": "Add", + "button.cancel": "Cancel", + "button.close": "Close", + "button.download": "Download", + "button.no": "No", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Save Settings", + "button.save.feed": "Save", + "button.state.adding": "Adding...", + "button.yes": "Yes", + "button.new": "New", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Connection could not be verified.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Connectivity Issue", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "Add Download Rule", + "feeds.add.feed": "Add Feed", + "feeds.applicable.feed": "Applicable Feed", + "feeds.apply.tags": "Apply Tags", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Exclude Pattern", + "feeds.existing.feeds": "Existing Feeds", + "feeds.existing.rules": "Existing Rules", + "feeds.interval": "Interval", + "feeds.label": "Label", + "feeds.match.count": "{count, plural, =1 {# match} other {# matches}}", + "feeds.match.pattern": "Match Pattern", + "feeds.match": "Match", + "feeds.exclude": "Exclude", + "feeds.no.feeds.available": "No feeds available.", + "feeds.no.feeds.defined": "No feeds defined.", + "feeds.no.items.matching": "No items matching search term.", + "feeds.no.rules.defined": "No rules defined.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Select Feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start on load", + "feeds.tabs.download.rules": "Download Rules", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent Feeds", + "feeds.tags": "Tags", + "feeds.test.match": "Test Match Pattern", + "feeds.time.hr": "Hours", + "feeds.time.min": "Minutes", + "feeds.time.day": "Days", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Search term", + "feeds.search.term": "Search term", + "feeds.validation.invalid.regular.expression": "Invalid regular expression.", + "feeds.validation.must.select.feed": "You must select a feed.", + "feeds.validation.must.specify.destination": "You must specify a destination.", + "feeds.validation.must.specify.label": "You must specify a label.", + "feeds.validation.must.specify.valid.feed.url": "You must specify a valid feed URL.", + "feeds.validation.interval.not.positive": "The interval must be a positive integer.", + "feeds.browse.feeds": "Browse feeds", + "filesystem.empty.directory": "Empty directory.", + "filesystem.error.eacces": "Flood does not have permission to read this directory.", + "filesystem.error.enoent": "This path does not exist. It will be created.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "Fetching directory structure...", + "filesystem.parent.directory": "Parent Directory", + "filter.all": "All", + "filter.status.title": "Filter by Status", + "filter.status.downloading": "Downloading", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Complete", + "filter.status.active": "Active", + "filter.status.inactive": "Inactive", + "filter.status.error": "Error", + "filter.status.stopped": "Stopped", + "filter.status.checking": "Checking", + "filter.tracker.title": "Filter by Tracker", + "filter.tag.title": "Filter by Tag", + "filter.untagged": "Untagged", + "general.ago": "ago", + "general.at": "at", + "general.to": "to", + "general.of": "of", + "general.clipboard.copy": "Copy", + "general.clipboard.copied": "Copied", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.", + "mediainfo.fetching": "Fetching...", + "mediainfo.heading": "Mediainfo Output", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Finished Downloading", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Error Reported", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Clear All", + "notification.showing": "Showing", + "priority.dont.download": "Don't Download", + "priority.high": "High", + "priority.low": "Low", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Download Slots Global", + "settings.bandwidth.slots.download.label": "Download Slots Per Torrent", + "settings.bandwidth.slots.heading": "Slot Availability", + "settings.bandwidth.slots.upload.global.label": "Upload Slots Global", + "settings.bandwidth.slots.upload.label": "Upload Slots Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown Presets: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown Presets: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Global Download Rate Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Global Upload Rate Throttle", + "settings.bandwidth.transferrate.heading": "Transfer Rate Throttles", + "settings.connectivity.dht.label": "Enable DHT", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentralized Peer Discovery", + "settings.connectivity.incoming.heading": "Incoming Connections", + "settings.connectivity.ip.hostname.label": "Reported IP/Hostname", + "settings.connectivity.max.http.connections": "Maximum HTTP Connections", + "settings.connectivity.peer.exchange.label": "Enable Peer Exchange", + "settings.connectivity.peers.desired.label": "Peers Desired", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maximum Peers", + "settings.connectivity.peers.min.label": "Minimum Peers", + "settings.connectivity.peers.seeding.max.label": "Maximum Peers Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimum Peers Seeding", + "settings.connectivity.port.open.label": "Open Port", + "settings.connectivity.port.randomize.label": "Randomize Port", + "settings.connectivity.port.range.label": "Port Range", + "settings.resources.disk.check.hash.label": "Verify Hash on Completion", + "settings.resources.disk.download.location.label": "Default Download Directory", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maximum Open Files", + "settings.resources.memory.heading": "Memory", + "settings.resources.memory.max.label": "Max Memory Usage", + "settings.tabs.bandwidth": "Bandwidth", + "settings.tabs.connectivity": "Connectivity", + "settings.tabs.heading": "Settings", + "settings.tabs.resources": "Resources", + "settings.tabs.authentication": "Authentication", + "settings.tabs.userinterface": "User Interface", + "settings.tabs.diskusage": "Disk Usage", + "settings.tabs.about": "About", + "settings.ui.locale": "Locale", + "settings.ui.language": "Language", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent List Display", + "settings.ui.torrent.size": "Torrent Size", + "settings.ui.torrent.size.expanded": "Expanded View", + "settings.ui.torrent.size.condensed": "Condensed View", + "settings.ui.torrent.details.enabled": "Enabled", + "settings.ui.torrent.details.tags.placement": "In the expanded view, tags work best at the end of the list.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Show", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "About Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Settings", + "sidebar.button.speedlimits": "Speed Limits", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log Out", + "sidebar.search.placeholder": "Search torrents", + "sidebar.transferdata.downloaded": "Downloaded", + "sidebar.transferdata.uploaded": "Uploaded", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Unlimited", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "hr", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Add Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Destination", + "torrents.add.destination.placeholder": "Destination", + "torrents.add.heading": "Add Torrents", + "torrents.add.start.label": "Start Torrent", + "torrents.add.tab.file.browse": "or click to browse", + "torrents.add.tab.file.drop": "Drop some files here,", + "torrents.add.tab.file.title": "By File", + "torrents.add.tab.url.input.placeholder": "Torrent URL or Magnet Link", + "torrents.add.tab.url.title": "By URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "Use as Base Path", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Details", + "torrents.details.files": "Files", + "torrents.details.files.loading": "Loading file detail...", + "torrents.details.files.download.file": "{count, plural, =1 {Download File} other {Download Files}}", + "torrents.details.general.comment": "Comment", + "torrents.details.general.connected": "{connected} connected of {total}", + "torrents.details.general.date.added": "Added", + "torrents.details.general.date.created": "Creation Date", + "torrents.details.general.downloaded": "Downloaded", + "torrents.details.general.free.disk.space": "Free Disk Space", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "General", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfer", + "torrents.details.general.location": "Location", + "torrents.details.general.none": "None", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignored", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Scheduler", + "torrents.details.general.seeds": "Seeds", + "torrents.details.general.size": "Size", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Tracker Message", + "torrents.details.general.type.private": "Private", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "There is no peer data for this torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} selected file} other {{countElement} selected files}}", + "torrents.details.selected.files.set.priority": "Set Priority", + "torrents.details.trackers.no.data": "There is no tracker data for this torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Clear Filters", + "torrents.list.context.check.hash": "Check Hash", + "torrents.list.context.details": "Torrent Details", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Set Torrent Location", + "torrents.list.context.pause": "Pause", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "Priority", + "torrents.list.context.remove": "Remove", + "torrents.list.context.set.tags": "Set Tags", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stop", + "torrents.list.no.torrents": "No torrents to display.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "of", + "torrents.move.button.set.location": "Set Location", + "torrents.move.button.state.setting": "Setting...", + "torrents.move.data.label": "Move data", + "torrents.move.check_hash.label": "Check hash", + "torrents.move.heading": "Set Torrent Location", + "torrents.properties.date.added": "Added", + "torrents.properties.comment": "Comment", + "torrents.properties.creation.date": "Creation Date", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Download Speed", + "torrents.properties.download.total": "Downloaded", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Free Disk Space", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignore Scheduler", + "torrents.properties.is.private": "Private", + "torrents.properties.name": "Name", + "torrents.properties.percentage": "Percent Complete", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "File Size", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Tracker Message", + "torrents.properties.upload.speed": "Upload Speed", + "torrents.properties.upload.total": "Uploaded", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Are you sure you want to remove {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Delete data", + "torrents.remove.error.no.torrents.selected": "You haven't selected any torrents.", + "torrents.remove": "Remove Torrents", + "torrents.set.tags.button.set": "Set Tags", + "torrents.set.tags.heading": "Set Tags", + "torrents.set.tags.enter.tags": "Enter tags", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "Sort By", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Disk Usage", + "status.diskusage.used": "Used", + "status.diskusage.free": "Free", + "status.diskusage.total": "Total", + "locale.language.auto": "Automatic", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Details", + "dependency.loading.transfer.history": "Data Transfer History", + "dependency.loading.torrent.list": "Torrent List" +} diff --git a/client/src/javascript/i18n/translations/cs.json b/client/src/javascript/i18n/translations/cs.json new file mode 100644 index 000000000..b4899f6b9 --- /dev/null +++ b/client/src/javascript/i18n/translations/cs.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Spustit Torrent", + "actionbar.button.stop.torrent": "Zastavit Torrent", + "actionbar.button.add.torrent": "Přidat Torrent", + "actionbar.button.remove.torrent": "Odebrat Torrent", + "alert.torrent.add": "Úspěšně přidáno {countElement} {count, plural, =1 {torrent} other {torrenty}}", + "alert.torrent.add.failed": "Nepodařilo se přidat {countElement} {count, plural, =1 {torrent} other {torrenty}}", + "alert.torrent.move": "Úspěšně přesunuto {countElement} {count, plural, =1 {torrent} other {torrenty}}", + "alert.torrent.move.failed": "Nepodařilo se přesunout {countElement} {count, plural, =1 {torrent} other {torrenty}}", + "alert.torrent.remove": "Úspěšně odstraněno {countElement} {count, plural, =1 {torrent} other {torrenty}}", + "alert.torrent.remove.failed": "Nepodařilo se odstranit {countElement} {count, plural, =1 {torrent} other {torrenty}}", + "alert.settings.saved": "Nastavení bylo úspěšně uloženo.", + "auth.add.user": "Přidat uživatele", + "auth.create.account": "Vytvořit účet", + "auth.create.an.account": "Vytvořit účet", + "auth.create.an.account.intro": "Vítejte v záplavě!", + "auth.current.user": "Aktuální uživatel", + "auth.error.username.empty": "Uživatelské jméno nemůže být prázdné.", + "auth.error.password.empty": "Heslo nemůže být prázdné.", + "auth.input.clear": "Vyčistit", + "auth.log.in": "Přihlásit se", + "auth.login": "Přihlásit se", + "auth.login.intro": "Přihlaste se ke svému účtu.", + "auth.password": "Heslo", + "auth.user.accounts": "Uživatelské účty", + "auth.username": "Uživatelské jméno", + "auth.admin": "Admin", + "auth.message.not.admin": "Uživatel není správce", + "button.add": "Přidat", + "button.cancel": "Zrušit", + "button.close": "Zavřít", + "button.download": "Stáhnout", + "button.no": "Ne", + "button.ok": "OK", + "button.retry": "Opakovat", + "button.save": "Uložit nastavení", + "button.save.feed": "Uložit", + "button.state.adding": "Přidávání...", + "button.yes": "Ano", + "button.new": "Nové", + "connection-interruption.action.selection.retry": "Opakovat s nastavením připojení aktuálního klienta", + "connection-interruption.action.selection.config": "Aktualizovat nastavení připojení klienta", + "connection-interruption.not.admin": "Pokud problém přetrvává, prosím kontaktujte administrátora.", + "connection-interruption.verification-error": "Nelze ověřit spojení.", + "connection.settings.client.select": "Klient", + "connection.settings.error.empty": "Nastavení připojení nemůže být prázdné.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Typ připojení", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Vystavení rTorrentu prostřednictvím TCP může umožnit eskalaci práv.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Hostitel", + "connection.settings.rtorrent.host.input.placeholder": "Hostname nebo IP adresa", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Cesta", + "connection.settings.rtorrent.socket.input.placeholder": "Cesta k socketu", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL webového API qBittorrentu", + "connection.settings.qbittorrent.username": "Uživatelské jméno", + "connection.settings.qbittorrent.username.input.placeholder": "Uživatelské jméno", + "connection.settings.qbittorrent.password": "Heslo", + "connection.settings.qbittorrent.password.input.placeholder": "Heslo", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL pro Transmission RPC rozhraní", + "connection.settings.transmission.username": "Uživatelské jméno", + "connection.settings.transmission.username.input.placeholder": "Uživatelské jméno", + "connection.settings.transmission.password": "Heslo", + "connection.settings.transmission.password.input.placeholder": "Heslo", + "connectivity.modal.title": "Problém s připojením", + "connectivity.modal.content": "Nelze se připojit k klientovi. Prosím aktualizujte nastavení připojení.", + "feeds.add.automatic.download.rule": "Přidat pravidlo pro stahování", + "feeds.add.feed": "Přidat kanál", + "feeds.applicable.feed": "Použitelný kanál", + "feeds.apply.tags": "Použít štítky", + "feeds.check": "Ověřte pravidlo vyzkoušením. Neuloženo ani neodesláno.", + "feeds.exclude.pattern": "Vyloučit vzor", + "feeds.existing.feeds": "Existující kanály", + "feeds.existing.rules": "Stávající pravidla", + "feeds.interval": "Interval", + "feeds.label": "Popisek", + "feeds.match.count": "{count, plural, =1 {# shoduje se s} other {# se shoduje s}}", + "feeds.match.pattern": "Vzor zápasu", + "feeds.match": "Zápas", + "feeds.exclude": "Vyjmout", + "feeds.no.feeds.available": "Žádné zdroje nejsou k dispozici.", + "feeds.no.feeds.defined": "Nebyly definovány žádné kanály.", + "feeds.no.items.matching": "Kritériím vyhledávání neodpovídají žádné položky.", + "feeds.no.rules.defined": "Nebyla definována žádná pravidla.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Vybrat kanál", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Spustit při načítání", + "feeds.tabs.download.rules": "Pravidla pro stahování", + "feeds.tabs.feeds": "Zdroje", + "feeds.tabs.heading": "Torrent kanály", + "feeds.tags": "Štítky", + "feeds.test.match": "Otestovat srovnávací vzor", + "feeds.time.hr": "Hodiny", + "feeds.time.min": "Zápis z jednání", + "feeds.time.day": "Dny", + "feeds.torrent.destination": "Cíl Torrentu", + "feeds.url": "URL", + "feeds.search": "Hledat termín", + "feeds.search.term": "Hledaný výraz", + "feeds.validation.invalid.regular.expression": "Neplatný regulární výraz.", + "feeds.validation.must.select.feed": "Musíte vybrat kanál.", + "feeds.validation.must.specify.destination": "Musíte zadat destinaci.", + "feeds.validation.must.specify.label": "Musíte zadat popisek.", + "feeds.validation.must.specify.valid.feed.url": "Musíte zadat platnou URL kanálu.", + "feeds.validation.interval.not.positive": "Interval musí být celé, kladné číslo.", + "feeds.browse.feeds": "Procházet kanály", + "filesystem.empty.directory": "Prázdný adresář.", + "filesystem.error.eacces": "Záplava nemá oprávnění ke čtení tohoto adresáře.", + "filesystem.error.enoent": "Tato cesta neexistuje. Bude vytvořena.", + "filesystem.error.unknown": "Došlo k neznámé chybě. Prosím, opakujte akci.", + "filesystem.fetching": "Načítání adresářové struktury...", + "filesystem.parent.directory": "Nadřazená složka", + "filter.all": "Vše", + "filter.status.title": "Filtrovat podle stavu", + "filter.status.downloading": "Stahování", + "filter.status.seeding": "Seedování", + "filter.status.completed": "Dokončeno", + "filter.status.active": "Aktivní", + "filter.status.inactive": "Neaktivní", + "filter.status.error": "Chyba", + "filter.status.stopped": "Zastaveno", + "filter.status.checking": "Kontrola", + "filter.tracker.title": "Filtrovat podle sledovače", + "filter.tag.title": "Filtrovat podle tagu", + "filter.untagged": "Neoznačeno", + "general.ago": "zpět", + "general.at": "v", + "general.to": "do", + "general.of": "z", + "general.clipboard.copy": "Kopírovat", + "general.clipboard.copied": "Zkopírováno", + "general.error.unknown": "Došlo k neznámé chybě", + "mediainfo.execError": "Došlo k chybě při běhu mediainfo na serveru. Zkontrolujte, zda je mediainfo nainstalováno a dostupné v PATH na Flood.", + "mediainfo.fetching": "Načítání...", + "mediainfo.heading": "Mediainfo výstup", + "notification.feed.torrent.added.heading": "Položka kanálu zařazena do fronty", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "Žádné oznámení k zobrazení.", + "notification.torrent.finished.heading": "Dokončené stahování", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Nahlášena chyba", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Vymazat vše", + "notification.showing": "Zobrazení", + "priority.dont.download": "Nestahovat", + "priority.high": "Vysoká", + "priority.low": "Nízká", + "priority.normal": "Normální", + "settings.bandwidth.slots.download.global.label": "Stáhnout sloty globálně", + "settings.bandwidth.slots.download.label": "Stáhnout Slot Per Torrent", + "settings.bandwidth.slots.heading": "Dostupnost slotu", + "settings.bandwidth.slots.upload.global.label": "Nahrát globální sloty", + "settings.bandwidth.slots.upload.label": "Nahrát sloty na Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Přednastavení rozbalovacího seznamu: Stáhnout", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Předvolby rozbalovacího seznamu: Nahrát", + "settings.bandwidth.transferrate.global.throttle.download": "Globální rychlost stahování plynu", + "settings.bandwidth.transferrate.global.throttle.upload": "Plyn globální rychlosti nahrávání", + "settings.bandwidth.transferrate.heading": "Převodní rychlost tahů", + "settings.connectivity.dht.label": "Povolit DHT", + "settings.connectivity.dht.port.label": "Port DHT", + "settings.connectivity.dpd.heading": "Decentralizované objevení Klienta", + "settings.connectivity.incoming.heading": "Příchozí připojení", + "settings.connectivity.ip.hostname.label": "Nahlášený IP/Hostname", + "settings.connectivity.max.http.connections": "Maximální HTTP připojení", + "settings.connectivity.peer.exchange.label": "Povolit Peer Exchange", + "settings.connectivity.peers.desired.label": "Klienti požadováni", + "settings.connectivity.peers.heading": "Klienti", + "settings.connectivity.peers.max.label": "Maximální počet klientů", + "settings.connectivity.peers.min.label": "Minimální počet klientů", + "settings.connectivity.peers.seeding.max.label": "Maximální počet Distribucí Klientů", + "settings.connectivity.peers.seeding.min.label": "Minimální počet Distribucí Klientů", + "settings.connectivity.port.open.label": "Otevřít port", + "settings.connectivity.port.randomize.label": "Náhodné nastavení portu", + "settings.connectivity.port.range.label": "Rozsah portu", + "settings.resources.disk.check.hash.label": "Ověřit hash po dokončení", + "settings.resources.disk.download.location.label": "Výchozí adresář pro stahování", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maximální počet otevřených souborů", + "settings.resources.memory.heading": "Paměť", + "settings.resources.memory.max.label": "Maximální využití paměti", + "settings.tabs.bandwidth": "Šířka pásma", + "settings.tabs.connectivity": "Připojení", + "settings.tabs.heading": "Nastavení", + "settings.tabs.resources": "Zdroje", + "settings.tabs.authentication": "Ověření", + "settings.tabs.userinterface": "Uživatelské rozhraní", + "settings.tabs.diskusage": "Využití disku", + "settings.tabs.about": "O aplikaci", + "settings.ui.locale": "Místní prostředí", + "settings.ui.language": "Jazyk", + "settings.ui.tag.selector.mode": "Předvolby výběru štítků", + "settings.ui.tag.selector.mode.single": "Výběr jedné možnosti", + "settings.ui.tag.selector.mode.multi": "Výběr více možností", + "settings.ui.torrent.list": "Zobrazení seznamu Torrentů", + "settings.ui.torrent.size": "Velikost Torrentu", + "settings.ui.torrent.size.expanded": "Rozšířené zobrazení", + "settings.ui.torrent.size.condensed": "Zkrácené zobrazení", + "settings.ui.torrent.details.enabled": "Povoleno", + "settings.ui.torrent.details.tags.placement": "V rozšířeném zobrazení, značky fungují nejlépe na konci seznamu.", + "settings.ui.torrent.context.menu.items.show": "Zobrazit", + "settings.ui.displayed.details": "Detailní torrent sloupce", + "settings.ui.displayed.context.menu.items": "Položky kontextové nabídky", + "settings.diskusage.show": "Zobrazit", + "settings.diskusage.mount.points": "Využití přípojných bodů disku", + "settings.about.flood": "O záplavách", + "sidebar.button.feeds": "Zdroje", + "sidebar.button.settings": "Nastavení", + "sidebar.button.speedlimits": "Omezení rychlosti", + "sidebar.button.theme.dark": "Tmavý motiv", + "sidebar.button.theme.light": "Světlý motiv", + "sidebar.button.log.out": "Odhlásit se", + "sidebar.search.placeholder": "Hledat torrenty", + "sidebar.transferdata.downloaded": "Staženo", + "sidebar.transferdata.uploaded": "Nahráno", + "sidebar.speedlimits.download": "STÁHNOUT", + "sidebar.speedlimits.upload": "NAHRÁT", + "speed.unlimited": "Bez omezení", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "CZ", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "let", + "unit.time.week": "tý", + "unit.time.day": "d", + "unit.time.hour": "hod.", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Přidat Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Volitelné cookie-name=cookie-value", + "torrents.add.destination.label": "Místo určení", + "torrents.add.destination.placeholder": "Místo určení", + "torrents.add.heading": "Přidat torrenty", + "torrents.add.start.label": "Spustit Torrent", + "torrents.add.tab.file.browse": "nebo klikněte pro procházení", + "torrents.add.tab.file.drop": "Sem přetáhněte některé soubory.", + "torrents.add.tab.file.title": "Podle souboru", + "torrents.add.tab.url.input.placeholder": "Torrent URL nebo Magnet odkaz", + "torrents.add.tab.url.title": "Podle URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Vytvořit", + "torrents.add.torrents.label": "Torrenty", + "torrents.add.tags": "Štítky", + "torrents.create.source.path.label": "Zdroj", + "torrents.create.trackers.label": "Trackery", + "torrents.create.tracker.input.placeholder": "URL Trackeru", + "torrents.create.base.name.label": "Základní název", + "torrents.create.base.name.input.placeholder": "Volitelný základní soubor nebo název adresáře torrentu", + "torrents.create.comment.label": "Komentář", + "torrents.create.comment.input.placeholder": "Volitelný komentář v souboru torrentu", + "torrents.create.info.source.label": "Zdroj informací", + "torrents.create.info.source.input.placeholder": "Volitelná zdrojová položka v infohashi", + "torrents.create.is.private.label": "Soukromé", + "torrents.create.tags.input.placeholder": "Štítky ve Floodu. Nepřidáno k vytvořenému torrentu.", + "torrents.destination.base_path": "Použít jako základní cestu", + "torrents.destination.completed": "Dokončeno", + "torrents.details.actions.pause": "Pozastavit", + "torrents.details.actions.start": "Začít", + "torrents.details.actions.stop": "Zastavit", + "torrents.details.details": "Detaily", + "torrents.details.files": "Soubory", + "torrents.details.files.loading": "Načítání detailu souboru...", + "torrents.details.files.download.file": "{count, plural, =1 {Stáhněte soubor} other {Stáhněte soubory}}", + "torrents.details.general.comment": "Komentář", + "torrents.details.general.connected": "{connected} připojen k {total}", + "torrents.details.general.date.added": "Přidáno", + "torrents.details.general.date.created": "Datum vytvoření", + "torrents.details.general.downloaded": "Staženo", + "torrents.details.general.free.disk.space": "Volné místo na disku", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Obecná ustanovení", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Sledování", + "torrents.details.general.heading.transfer": "Převod", + "torrents.details.general.location": "Poloha", + "torrents.details.general.none": "Nic", + "torrents.details.general.peers": "Klienti", + "torrents.details.general.scheduler.ignored": "Ignorováno", + "torrents.details.general.scheduler.obeyed": "Ovesné", + "torrents.details.general.scheduler": "Plánovač", + "torrents.details.general.seeds": "Semena", + "torrents.details.general.size": "Velikost", + "torrents.details.general.tags": "Štítky", + "torrents.details.general.tracker.message": "Sledovací zpráva", + "torrents.details.general.type.private": "Soukromé", + "torrents.details.general.type.public": "Veřejnost", + "torrents.details.general.type": "Typ", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Pro tento torrent nejsou k dispozici žádná klientská data.", + "torrents.details.peers": "Klienti", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} vybral soubor} other {{countElement} vybrané soubory}}", + "torrents.details.selected.files.set.priority": "Nastavit prioritu", + "torrents.details.trackers.no.data": "Pro tento torrent nejsou k dispozici žádná data trackeru.", + "torrents.details.trackers.type": "Typ", + "torrents.details.trackers": "Sledovače", + "torrents.generate.magnet.heading": "Vygenerovat magnet odkaz", + "torrents.generate.magnet.loading.trackers": "Načítání trackerů...", + "torrents.generate.magnet.private.torrent": "Toto je soukromý torrent.", + "torrents.generate.magnet.magnet": "Magnet odkaz", + "torrents.generate.magnet.magnet.with.trackers": "Magnet odkaz s trackery", + "torrents.list.clear.filters": "Vymazat filtry", + "torrents.list.context.check.hash": "Zkontrolovat Hash", + "torrents.list.context.details": "Podrobnosti Torrentu", + "torrents.list.context.generate.magnet": "Vygenerovat magnet odkaz", + "torrents.list.context.move": "Nastavit umístění Torrentu", + "torrents.list.context.pause": "Pozastavit", + "torrents.list.context.download": "Stáhnout", + "torrents.list.context.priority": "Priorita", + "torrents.list.context.remove": "Odebrat", + "torrents.list.context.set.tags": "Nastavit štítky", + "torrents.list.context.set.trackers": "Nastavit Trackery", + "torrents.list.context.start": "Začít", + "torrents.list.context.stop": "Zastavit", + "torrents.list.no.torrents": "Žádné torrenty k zobrazení.", + "torrents.list.drop": "Přetáhněte soubory sem pro přidání.", + "torrents.list.cannot.connect": "Nelze se připojit ke klientu.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "z", + "torrents.move.button.set.location": "Nastavit umístění", + "torrents.move.button.state.setting": "Nastavení...", + "torrents.move.data.label": "Přesunout data", + "torrents.move.check_hash.label": "Zkontrolovat hash", + "torrents.move.heading": "Nastavit umístění Torrentu", + "torrents.properties.date.added": "Přidáno", + "torrents.properties.comment": "Komentář", + "torrents.properties.creation.date": "Datum vytvoření", + "torrents.properties.directory": "Poloha", + "torrents.properties.download.speed": "Rychlost stahování", + "torrents.properties.download.total": "Staženo", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Volné místo na disku", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignorovat plánovač", + "torrents.properties.is.private": "Soukromé", + "torrents.properties.name": "Název", + "torrents.properties.percentage": "Procento dokončeno", + "torrents.properties.ratio": "Poměr", + "torrents.properties.size": "Velikost souboru", + "torrents.properties.tags": "Štítky", + "torrents.properties.tracker.message": "Sledovací zpráva", + "torrents.properties.upload.speed": "Rychlost nahrávání", + "torrents.properties.upload.total": "Nahráno", + "torrents.properties.seeds": "Seedy", + "torrents.properties.peers": "Peery", + "torrents.properties.trackers": "Trackery", + "torrents.remove.are.you.sure": "Jste si jisti, že chcete odstranit {count, plural, =1 {# torrent} other {# torrentů}}?", + "torrents.remove.delete.data": "Odstranit data", + "torrents.remove.error.no.torrents.selected": "Nevybrali jste žádné torrenty.", + "torrents.remove": "Odebrat Torrenty", + "torrents.set.tags.button.set": "Nastavit štítky", + "torrents.set.tags.heading": "Nastavit štítky", + "torrents.set.tags.enter.tags": "Zadejte štítky", + "torrents.set.trackers.button.set": "Nastavit Trackery", + "torrents.set.trackers.heading": "Nastavit Trackery", + "torrents.set.trackers.enter.tracker": "Vložit tracker", + "torrents.set.trackers.loading.trackers": "Načítání trackerů...", + "torrents.sort.title": "Seřadit podle", + "connection-interruption.heading": "Nelze se připojit ke klientu", + "status.diskusage.title": "Využití disku", + "status.diskusage.used": "Použité", + "status.diskusage.free": "Volné", + "status.diskusage.total": "Celkem", + "locale.language.auto": "Automaticky", + "locale.language.translate": "Začít překládat", + "dependency.loading.notifications": "Oznámení", + "dependency.loading.torrent.taxonomy": "Taxonomie Torrentu", + "dependency.loading.transfer.rate.details": "Podrobnosti o rychlosti přenosu dat", + "dependency.loading.transfer.history": "Historie přenosu dat", + "dependency.loading.torrent.list": "List Torrentů" +} diff --git a/client/src/javascript/i18n/translations/da.json b/client/src/javascript/i18n/translations/da.json new file mode 100644 index 000000000..2bd6df2c5 --- /dev/null +++ b/client/src/javascript/i18n/translations/da.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Tilføj Torrent", + "actionbar.button.remove.torrent": "Fjern Torrent", + "alert.torrent.add": "Tilføjet {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Mislykkedes at tilføje {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Flyttede {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move.failed": "Mislykkedes at flytte {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Slettede {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Mislykkedes at fjerne {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Indstillingerne er gemt korrekt.", + "auth.add.user": "Tilføj Bruger", + "auth.create.account": "Opret Konto", + "auth.create.an.account": "Opret en konto", + "auth.create.an.account.intro": "Velkommen til Flood!", + "auth.current.user": "Nuværende Bruger", + "auth.error.username.empty": "Brugernavn må ikke være tomt.", + "auth.error.password.empty": "Adgangskoden må ikke være tom.", + "auth.input.clear": "Clear", + "auth.log.in": "Log Ind", + "auth.login": "Login", + "auth.login.intro": "Log ind på din konto.", + "auth.password": "Adgangskode", + "auth.user.accounts": "Bruger Konti", + "auth.username": "Brugernavn", + "auth.admin": "Administrator", + "auth.message.not.admin": "Brugeren er ikke Admin", + "button.add": "Tilføj", + "button.cancel": "Annuller", + "button.close": "Close", + "button.download": "Hent", + "button.no": "Nej", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Gem Indstillinger", + "button.save.feed": "Gem", + "button.state.adding": "Tilføjer...", + "button.yes": "Ja", + "button.new": "Ny", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Forbindelsen kunne ikke verificeres.", + "connection.settings.client.select": "Klient", + "connection.settings.error.empty": "Forbindelsesindstillinger kan ikke være tomme.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Forbindelsestype", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Vært", + "connection.settings.rtorrent.host.input.placeholder": "Værtsnavn eller IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Sti", + "connection.settings.rtorrent.socket.input.placeholder": "Sti til sokkel", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL til qBittorrent Web API", + "connection.settings.qbittorrent.username": "Brugernavn", + "connection.settings.qbittorrent.username.input.placeholder": "Brugernavn", + "connection.settings.qbittorrent.password": "Adgangskode", + "connection.settings.qbittorrent.password.input.placeholder": "Adgangskode", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Tilslutningsproblem", + "connectivity.modal.content": "Kan ikke oprette forbindelse til klienten. Opdater forbindelsesindstillinger.", + "feeds.add.automatic.download.rule": "Tilføj Download Regel", + "feeds.add.feed": "Tilføj Feed", + "feeds.applicable.feed": "Gældende Feed", + "feeds.apply.tags": "Anvend Tags", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Udeluk Mønster", + "feeds.existing.feeds": "Eksisterende Feeds", + "feeds.existing.rules": "Eksisterende Regler", + "feeds.interval": "Interval", + "feeds.label": "Etiket", + "feeds.match.count": "{count, plural, =1 {# match} other {# match}}", + "feeds.match.pattern": "Match Mønster", + "feeds.match": "Match", + "feeds.exclude": "Udeluk", + "feeds.no.feeds.available": "Ingen tilgængelige feeds.", + "feeds.no.feeds.defined": "Ingen feeds defineret.", + "feeds.no.items.matching": "Ingen elementer, der matcher søgeord.", + "feeds.no.rules.defined": "Ingen regler defineret.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Vælg Feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start ved indlæsning", + "feeds.tabs.download.rules": "Download Regler", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent Feeds", + "feeds.tags": "Mærker", + "feeds.test.match": "Test Match Mønster", + "feeds.time.hr": "Timer", + "feeds.time.min": "Minutter", + "feeds.time.day": "Dage", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Søgeord", + "feeds.search.term": "Søgeord", + "feeds.validation.invalid.regular.expression": "Ugyldigt regulært udtryk.", + "feeds.validation.must.select.feed": "Du skal vælge et feed.", + "feeds.validation.must.specify.destination": "Du skal angive en destination.", + "feeds.validation.must.specify.label": "Du skal angive en etiket.", + "feeds.validation.must.specify.valid.feed.url": "Du skal angive en gyldig feed-URL.", + "feeds.validation.interval.not.positive": "Intervallet skal være et positivt heltal.", + "feeds.browse.feeds": "Gennemse feeds", + "filesystem.empty.directory": "Tom mappe.", + "filesystem.error.eacces": "Oversvømmelse har ikke tilladelse til at læse denne mappe.", + "filesystem.error.enoent": "Denne sti eksisterer ikke. Den vil blive oprettet.", + "filesystem.error.unknown": "Der opstod en ukendt fejl. Prøv venligst igen.", + "filesystem.fetching": "Henter mappestruktur...", + "filesystem.parent.directory": "Overordnet Mappe", + "filter.all": "Alle", + "filter.status.title": "Filtrer efter status", + "filter.status.downloading": "Downloader", + "filter.status.seeding": "Seeder", + "filter.status.completed": "Complete", + "filter.status.active": "Aktiv", + "filter.status.inactive": "Inaktiv", + "filter.status.error": "Fejl", + "filter.status.stopped": "Stoppet", + "filter.status.checking": "Kontrol", + "filter.tracker.title": "Filtrer efter Tracker", + "filter.tag.title": "Filtrer efter tag", + "filter.untagged": "Utagget", + "general.ago": "siden", + "general.at": "på", + "general.to": "til", + "general.of": "af", + "general.clipboard.copy": "Kopiér", + "general.clipboard.copied": "Kopieret", + "general.error.unknown": "Der opstod en ukendt fejl", + "mediainfo.execError": "Der opstod en fejl under kørsel af mediainfo på serveren. Kontroller at mediainfo er installeret og tilgængelig i PATH til Flood.", + "mediainfo.fetching": "Henter...", + "mediainfo.heading": "Mediainfo Output", + "notification.feed.torrent.added.heading": "Feed Element I Køen", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Download Færdig", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Fejl Rapporteret", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Ryd Alle", + "notification.showing": "Viser", + "priority.dont.download": "Download Ikke", + "priority.high": "Høj", + "priority.low": "Lav", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Download Slots Globalt", + "settings.bandwidth.slots.download.label": "Download Slots Pr. Torrent", + "settings.bandwidth.slots.heading": "Tilgængelighed På Slot", + "settings.bandwidth.slots.upload.global.label": "Upload Slots Global", + "settings.bandwidth.slots.upload.label": "Upload Slots Pr. Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown Forudindstillinger: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown Forudindstillinger: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Global Downloadhastighed Gas", + "settings.bandwidth.transferrate.global.throttle.upload": "Global Upload Rate Gas", + "settings.bandwidth.transferrate.heading": "Overførselsrate Throttles", + "settings.connectivity.dht.label": "Aktiver DHT", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentraliseret Peer Discovery", + "settings.connectivity.incoming.heading": "Indgående Forbindelser", + "settings.connectivity.ip.hostname.label": "Rapporteret Ip/Værtsnavn", + "settings.connectivity.max.http.connections": "Maksimum Http-forbindelser", + "settings.connectivity.peer.exchange.label": "Aktiver Peer Exchange", + "settings.connectivity.peers.desired.label": "Peers Ønsket", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maksimum Peers", + "settings.connectivity.peers.min.label": "Mindste Peers", + "settings.connectivity.peers.seeding.max.label": "Maksimum Peers Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimum Peers Seeding", + "settings.connectivity.port.open.label": "Åbn Port", + "settings.connectivity.port.randomize.label": "Tilfældig Port", + "settings.connectivity.port.range.label": "Port Range", + "settings.resources.disk.check.hash.label": "Verificér Hash ved fuldførelse", + "settings.resources.disk.download.location.label": "Standard Download Mappe", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maksimum Åbne Filer", + "settings.resources.memory.heading": "Hukommelse", + "settings.resources.memory.max.label": "Maks. Hukommelsesforbrug", + "settings.tabs.bandwidth": "Båndbredde", + "settings.tabs.connectivity": "Tilslutning", + "settings.tabs.heading": "Indstillinger", + "settings.tabs.resources": "Ressourcer", + "settings.tabs.authentication": "Godkendelse", + "settings.tabs.userinterface": "Brugergrænseflade", + "settings.tabs.diskusage": "Diskforbrug", + "settings.tabs.about": "Om", + "settings.ui.locale": "Landestandard", + "settings.ui.language": "Sprog", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent Liste Visning", + "settings.ui.torrent.size": "Torrent Størrelse", + "settings.ui.torrent.size.expanded": "Udvidet Visning", + "settings.ui.torrent.size.condensed": "Kondenseret Visning", + "settings.ui.torrent.details.enabled": "Aktiveret", + "settings.ui.torrent.details.tags.placement": "I den udvidede visning fungerer tags bedst i slutningen af listen.", + "settings.ui.torrent.context.menu.items.show": "Vis", + "settings.ui.displayed.details": "Torrent Detaljekolonner", + "settings.ui.displayed.context.menu.items": "Kontekst Menupunkter", + "settings.diskusage.show": "Vis", + "settings.diskusage.mount.points": "Monteringspunkter For Diskforbrug", + "settings.about.flood": "Om Oversvømmelse", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Indstillinger", + "sidebar.button.speedlimits": "Grænser For Hastighed", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log Ud", + "sidebar.search.placeholder": "Søg efter torrents", + "sidebar.transferdata.downloaded": "Downloadet", + "sidebar.transferdata.uploaded": "Uploadet", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Ubegrænset", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "uge", + "unit.time.day": "d", + "unit.time.hour": "time", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Tilføj Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Valgfri cookie-name=cookie-value", + "torrents.add.destination.label": "Bestemmelse", + "torrents.add.destination.placeholder": "Bestemmelse", + "torrents.add.heading": "Tilføj Torrents", + "torrents.add.start.label": "Start Torrent", + "torrents.add.tab.file.browse": "eller klik for at gennemse", + "torrents.add.tab.file.drop": "Slip nogle filer her,", + "torrents.add.tab.file.title": "Efter Fil", + "torrents.add.tab.url.input.placeholder": "Torrent URL eller Magnet Link", + "torrents.add.tab.url.title": "Efter URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Opret", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Mærker", + "torrents.create.source.path.label": "Kilde", + "torrents.create.trackers.label": "Trackere", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Basis Navn", + "torrents.create.base.name.input.placeholder": "Valgfri basisfil eller mappenavn for torrent", + "torrents.create.comment.label": "Kommentar", + "torrents.create.comment.input.placeholder": "Valgfri kommentar i torrent fil", + "torrents.create.info.source.label": "Info Kilde", + "torrents.create.info.source.input.placeholder": "Valgfri kildeindtastning i infohash", + "torrents.create.is.private.label": "Privat", + "torrents.create.tags.input.placeholder": "Tags i Flood. Ikke tilføjet til oprettet torrent.", + "torrents.destination.base_path": "Brug som basissti", + "torrents.destination.completed": "Afsluttet", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Detaljer", + "torrents.details.files": "Filer", + "torrents.details.files.loading": "Indlæser fildetaljer...", + "torrents.details.files.download.file": "{count, plural, =1 {Download Fil} other {Download Filer}}", + "torrents.details.general.comment": "Kommentar", + "torrents.details.general.connected": "{connected} forbundet af {total}", + "torrents.details.general.date.added": "Tilføjet", + "torrents.details.general.date.created": "Oprettelsesdato", + "torrents.details.general.downloaded": "Downloadet", + "torrents.details.general.free.disk.space": "Ledig Diskplads", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Generelt", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Overfør", + "torrents.details.general.location": "Placering", + "torrents.details.general.none": "Ingen", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignoreret", + "torrents.details.general.scheduler.obeyed": "Afskallet", + "torrents.details.general.scheduler": "Planlægger", + "torrents.details.general.seeds": "Frø", + "torrents.details.general.size": "Størrelse", + "torrents.details.general.tags": "Mærker", + "torrents.details.general.tracker.message": "Tracker Besked", + "torrents.details.general.type.private": "Privat", + "torrents.details.general.type.public": "Offentlig", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Der er ingen peer data for denne torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} valgt fil} other {{countElement} valgte filer}}", + "torrents.details.selected.files.set.priority": "Angiv Prioritet", + "torrents.details.trackers.no.data": "Der er ingen tracker data for denne torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackere", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Ryd Filtre", + "torrents.list.context.check.hash": "Tjek Hash", + "torrents.list.context.details": "Torrent Detaljer", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Angiv Torrent Placering", + "torrents.list.context.pause": "Pause", + "torrents.list.context.download": "Hent", + "torrents.list.context.priority": "Prioritet", + "torrents.list.context.remove": "Fjern", + "torrents.list.context.set.tags": "Sæt Tags", + "torrents.list.context.set.trackers": "Indstil Trackers", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stop", + "torrents.list.no.torrents": "Ingen torrents at vise.", + "torrents.list.drop": "Slip filer her for at tilføje dem.", + "torrents.list.cannot.connect": "Kan ikke forbinde til klienten.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "af", + "torrents.move.button.set.location": "Angiv Placering", + "torrents.move.button.state.setting": "Indstillinger...", + "torrents.move.data.label": "Flyt data", + "torrents.move.check_hash.label": "Tjek hash", + "torrents.move.heading": "Angiv Torrent Placering", + "torrents.properties.date.added": "Tilføjet", + "torrents.properties.comment": "Kommentar", + "torrents.properties.creation.date": "Oprettelsesdato", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Download Hastighed", + "torrents.properties.download.total": "Downloadet", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Ledig Diskplads", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignorer Skemalægning", + "torrents.properties.is.private": "Privat", + "torrents.properties.name": "Navn", + "torrents.properties.percentage": "Procent Fuldført", + "torrents.properties.ratio": "Forhold", + "torrents.properties.size": "Fil Størrelse", + "torrents.properties.tags": "Mærker", + "torrents.properties.tracker.message": "Tracker Besked", + "torrents.properties.upload.speed": "Upload Hastighed", + "torrents.properties.upload.total": "Uploadet", + "torrents.properties.seeds": "Frø", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackere", + "torrents.remove.are.you.sure": "Er du sikker på, at du vil fjerne {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Slet data", + "torrents.remove.error.no.torrents.selected": "Du har ikke valgt nogen torrents.", + "torrents.remove": "Fjern Torrents", + "torrents.set.tags.button.set": "Sæt Tags", + "torrents.set.tags.heading": "Sæt Tags", + "torrents.set.tags.enter.tags": "Indtast tags", + "torrents.set.trackers.button.set": "Indstil Trackers", + "torrents.set.trackers.heading": "Indstil Trackers", + "torrents.set.trackers.enter.tracker": "Indtast en tracker", + "torrents.set.trackers.loading.trackers": "Indlæser trackere...", + "torrents.sort.title": "Sortér Efter", + "connection-interruption.heading": "Kan ikke oprette forbindelse til klienten", + "status.diskusage.title": "Diskforbrug", + "status.diskusage.used": "Brugt", + "status.diskusage.free": "Gratis", + "status.diskusage.total": "I Alt", + "locale.language.auto": "Automatisk", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifikationer", + "dependency.loading.torrent.taxonomy": "Torrent Taksonomi", + "dependency.loading.transfer.rate.details": "Oplysninger Om Dataoverførselsrate", + "dependency.loading.transfer.history": "Data Overførselshistorik", + "dependency.loading.torrent.list": "Torrent Liste" +} diff --git a/client/src/javascript/i18n/translations/de.json b/client/src/javascript/i18n/translations/de.json new file mode 100644 index 000000000..805fd5622 --- /dev/null +++ b/client/src/javascript/i18n/translations/de.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Torrent starten", + "actionbar.button.stop.torrent": "Torrent stoppen", + "actionbar.button.add.torrent": "Torrent hinzufügen", + "actionbar.button.remove.torrent": "Torrent entfernen", + "alert.torrent.add": "{countElement} {count, plural, =1 {Torrent} other {Torrents}} erfolgreich hinzugefügt.", + "alert.torrent.add.failed": "Hinzufügen von {countElement} {count, plural, =1 {Torrent} other {Torrents}} fehlgeschlagen.", + "alert.torrent.move": "{countElement} {count, plural, =1 {Torrent} other {Torrents}} erfolgreich verschoben.", + "alert.torrent.move.failed": "Verschieben von {countElement} {count, plural, =1 {Torrent} other {Torrents}} fehlgeschlagen.", + "alert.torrent.remove": "{countElement} {count, plural, =1 {Torrent} other {Torrents}} erfolgreich entfernt.", + "alert.torrent.remove.failed": "Entfernen von {countElement} {count, plural, =1 {Torrent} other {Torrents}} fehlgeschlagen.", + "alert.settings.saved": "Einstellungen erfolgreich gespeichert.", + "auth.add.user": "Benutzer hinzufügen", + "auth.create.account": "Konto erstellen", + "auth.create.an.account": "Ein Konto erstellen", + "auth.create.an.account.intro": "Willkommen bei Flood!", + "auth.current.user": "Aktueller Benutzer", + "auth.error.username.empty": "Benutzername darf nicht leer sein.", + "auth.error.password.empty": "Passwort darf nicht leer sein.", + "auth.input.clear": "Löschen", + "auth.log.in": "Anmelden", + "auth.login": "Anmelden", + "auth.login.intro": "Melden Sie sich bei Ihrem Konto an.", + "auth.password": "Passwort", + "auth.user.accounts": "Benutzerkonten", + "auth.username": "Benutzername", + "auth.admin": "Admin", + "auth.message.not.admin": "Benutzer ist kein Admin", + "button.add": "Hinzufügen", + "button.cancel": "Abbrechen", + "button.close": "Schließen", + "button.download": "Download", + "button.no": "Nein", + "button.ok": "OK", + "button.retry": "Erneut versuchen", + "button.save": "Einstellungen speichern", + "button.save.feed": "Speichern", + "button.state.adding": "Hinzufügen...", + "button.yes": "Ja", + "button.new": "Neu", + "connection-interruption.action.selection.retry": "Mit aktuellen Clientverbindungseinstellungen erneut versuchen", + "connection-interruption.action.selection.config": "Clientverbindungseinstellungen aktualisieren", + "connection-interruption.not.admin": "Bitte kontaktieren Sie Ihren Flood-Administrator, wenn dies weiterhin geschieht.", + "connection-interruption.verification-error": "Verbindung konnte nicht hergestellt werden.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Verbindungseinstellungen dürfen nicht leer sein.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Verbindungstyp", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Das Bereitstellen von rTorrent via TCP kann zu einer Privilegien-Eskalation führen.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname oder IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Pfad", + "connection.settings.rtorrent.socket.input.placeholder": "Pfad zum Socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL zur qBittorrent Web API", + "connection.settings.qbittorrent.username": "Benutzername", + "connection.settings.qbittorrent.username.input.placeholder": "Benutzername", + "connection.settings.qbittorrent.password": "Passwort", + "connection.settings.qbittorrent.password.input.placeholder": "Passwort", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL zur Transmission RPC-Schnittstelle", + "connection.settings.transmission.username": "Benutzername", + "connection.settings.transmission.username.input.placeholder": "Benutzername", + "connection.settings.transmission.password": "Passwort", + "connection.settings.transmission.password.input.placeholder": "Passwort", + "connectivity.modal.title": "Verbindungsproblem", + "connectivity.modal.content": "Keine Verbindung zum Client. Bitte Verbindungseinstellungen aktualisieren.", + "feeds.add.automatic.download.rule": "Downloadregel hinzufügen", + "feeds.add.feed": "Feed hinzufügen", + "feeds.applicable.feed": "Zutreffender Feed", + "feeds.apply.tags": "Tags anwenden", + "feeds.check": "Überprüfe die Regel durch ausprobieren. Wird weder gespeichert noch gesendet.", + "feeds.exclude.pattern": "Muster ausschließen", + "feeds.existing.feeds": "Vorhandene Feeds", + "feeds.existing.rules": "Vorhandene Regeln", + "feeds.interval": "Intervall", + "feeds.label": "Label", + "feeds.match.count": "{count, plural, =1 {# Übereinstimmung} other {# Übereinstimmungen}}", + "feeds.match.pattern": "Match-Muster", + "feeds.match": "Übereinstimmung", + "feeds.exclude": "Ausschließen", + "feeds.no.feeds.available": "Keine Feeds verfügbar.", + "feeds.no.feeds.defined": "Keine Feeds definiert.", + "feeds.no.items.matching": "Keine Artikel stimmen mit dem Suchbegriff überein.", + "feeds.no.rules.defined": "Keine Regeln definiert.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Feed auswählen", + "feeds.select.interval": "Intervall", + "feeds.start.on.load": "Beim Abrufen starten", + "feeds.tabs.download.rules": "Downloadregeln", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent-Feeds", + "feeds.tags": "Tags", + "feeds.test.match": "Test-Match-Muster", + "feeds.time.hr": "Stunden", + "feeds.time.min": "Minuten", + "feeds.time.day": "Tage", + "feeds.torrent.destination": "Downloadpfad des Torrents", + "feeds.url": "URL", + "feeds.search": "Suchbegriff", + "feeds.search.term": "Suchbegriff", + "feeds.validation.invalid.regular.expression": "Ungültiger regulärer Ausdruck.", + "feeds.validation.must.select.feed": "Sie müssen einen Feed auswählen.", + "feeds.validation.must.specify.destination": "Sie müssen einen Downloadpfad angeben.", + "feeds.validation.must.specify.label": "Sie müssen ein Label angeben.", + "feeds.validation.must.specify.valid.feed.url": "Sie müssen eine gültige Feed-URL angeben.", + "feeds.validation.interval.not.positive": "Das Intervall muss eine positive Ganzzahl sein.", + "feeds.browse.feeds": "Feeds durchsuchen", + "filesystem.empty.directory": "Leeres Verzeichnis.", + "filesystem.error.eacces": "Flood hat keine Berechtigung, dieses Verzeichnis zu lesen.", + "filesystem.error.enoent": "Dieser Pfad existiert nicht. Er wird erstellt.", + "filesystem.error.unknown": "Ein unbekannter Fehler ist aufgetreten. Bitte erneut versuchen.", + "filesystem.fetching": "Lade Verzeichnisstruktur...", + "filesystem.parent.directory": "Übergeordnetes Verzeichnis", + "filter.all": "Alle", + "filter.status.title": "Nach Status filtern", + "filter.status.downloading": "Beim Herunterladen", + "filter.status.seeding": "Seede", + "filter.status.completed": "Abgeschlossen", + "filter.status.active": "Aktiv", + "filter.status.inactive": "Inaktiv", + "filter.status.error": "Fehlerhaft", + "filter.status.stopped": "Gestoppt", + "filter.status.checking": "Prüfe", + "filter.tracker.title": "Nach Tracker filtern", + "filter.tag.title": "Nach Tag filtern", + "filter.untagged": "Ohne Tag", + "general.ago": "vor", + "general.at": "am", + "general.to": "zu", + "general.of": "von", + "general.clipboard.copy": "Kopieren", + "general.clipboard.copied": "Kopiert", + "general.error.unknown": "Ein unbekannter Fehler ist aufgetreten", + "mediainfo.execError": "Beim Ausführen von mediainfo auf dem Server ist ein Fehler aufgetreten. Überprüfen Sie, ob mediainfo installiert ist und im PATH to Flood verfügbar ist.", + "mediainfo.fetching": "Abrufen...", + "mediainfo.heading": "Ausgabe von Mediainfo", + "notification.feed.torrent.added.heading": "Feed Artikel wurde hinzugefügt", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "Keine anzuzeigenden Benachrichtigungen.", + "notification.torrent.finished.heading": "Download abgeschlossen", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Fehler gemeldet", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Alle löschen", + "notification.showing": "Anzeigen", + "priority.dont.download": "Nicht herunterladen", + "priority.high": "Hoch", + "priority.low": "Niedrig", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Maximale globale Anzahl von DL-Slots", + "settings.bandwidth.slots.download.label": "Maximale Anzahl von DL-Slots pro Torrent", + "settings.bandwidth.slots.heading": "Slot-Beschränkungen", + "settings.bandwidth.slots.upload.global.label": "Maximale globale Anzahl von UL-Slots", + "settings.bandwidth.slots.upload.label": "Maximale Anzahl von UL-Slots pro Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown-Voreinstellungen: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown-Voreinstellungen: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Begrenzung der globalen DL-Rate", + "settings.bandwidth.transferrate.global.throttle.upload": "Begrenzung der globalen UL-Rate", + "settings.bandwidth.transferrate.heading": "Verbindungsbeschränkungen", + "settings.connectivity.dht.label": "DHT aktivieren", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Dezentralisiertes Netzwerk", + "settings.connectivity.incoming.heading": "Eingehende Verbindungen", + "settings.connectivity.ip.hostname.label": "Gemeldete/r IP/Hostname", + "settings.connectivity.max.http.connections": "Maximale Anzahl von HTTP-Verbindungen", + "settings.connectivity.peer.exchange.label": "Peer-Exchange aktivieren", + "settings.connectivity.peers.desired.label": "Gewünschte Anzahl von Peers", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maximale Anzahl von Peers", + "settings.connectivity.peers.min.label": "Minimale Anzahl von Peers", + "settings.connectivity.peers.seeding.max.label": "Maximale Anzahl von Peers für Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimale Anzahl von Peers für Seeding", + "settings.connectivity.port.open.label": "Offener Port", + "settings.connectivity.port.randomize.label": "Zufälliger Port", + "settings.connectivity.port.range.label": "Port-Bereich", + "settings.resources.disk.check.hash.label": "Hash nach Abschluss verifizieren", + "settings.resources.disk.download.location.label": "Standard-Download-Verzeichnis", + "settings.resources.disk.heading": "Speicher", + "settings.resources.max.open.files": "Maximal geöffnete Dateien", + "settings.resources.memory.heading": "Arbeitsspeicher", + "settings.resources.memory.max.label": "Maximale Speichernutzung", + "settings.tabs.bandwidth": "Geschwindigkeit", + "settings.tabs.connectivity": "Verbindung", + "settings.tabs.heading": "Einstellungen", + "settings.tabs.resources": "Ressourcen", + "settings.tabs.authentication": "Authentifizierung", + "settings.tabs.userinterface": "Benutzeroberfläche", + "settings.tabs.diskusage": "Speichernutzung", + "settings.tabs.about": "Über", + "settings.ui.locale": "Lokalisierung", + "settings.ui.language": "Sprache", + "settings.ui.tag.selector.mode": "Bevorzugte Tag-Auswahl", + "settings.ui.tag.selector.mode.single": "Einzelauswahl", + "settings.ui.tag.selector.mode.multi": "Mehrfachauswahl", + "settings.ui.torrent.list": "Anzeige der Torrent-Liste", + "settings.ui.torrent.size": "Torrent-Größe", + "settings.ui.torrent.size.expanded": "Erweitertes Layout", + "settings.ui.torrent.size.condensed": "Kompaktes Layout", + "settings.ui.torrent.details.enabled": "Aktiv", + "settings.ui.torrent.details.tags.placement": "In der erweiterten Ansicht funktionieren Tags am Ende der Liste am besten.", + "settings.ui.torrent.context.menu.items.show": "Anzeigen", + "settings.ui.displayed.details": "Detailspalten der Torrent-Liste", + "settings.ui.displayed.context.menu.items": "Einträge des Kontextmenüs", + "settings.diskusage.show": "Anzeigen", + "settings.diskusage.mount.points": "Einhängepunkte für Speichernutzung", + "settings.about.flood": "Über Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Einstellungen", + "sidebar.button.speedlimits": "Geschwindigkeitslimits", + "sidebar.button.theme.dark": "Dunkles Design", + "sidebar.button.theme.light": "Helles Design", + "sidebar.button.log.out": "Abmelden", + "sidebar.search.placeholder": "Suche Torrents", + "sidebar.transferdata.downloaded": "Heruntergeladen", + "sidebar.transferdata.uploaded": "Hochgeladen", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Unbegrenzt", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "h", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Torrent hinzufügen", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional Cookie-Name=Cookie-Wert", + "torrents.add.destination.label": "Downloadpfad", + "torrents.add.destination.placeholder": "Downloadpfad", + "torrents.add.heading": "Torrents hinzufügen", + "torrents.add.start.label": "Torrent starten", + "torrents.add.tab.file.browse": "oder klicken, um zu durchsuchen", + "torrents.add.tab.file.drop": "Dateien hier ablegen", + "torrents.add.tab.file.title": "Von Datei", + "torrents.add.tab.url.input.placeholder": "Torrent-URL oder Magnet-Link", + "torrents.add.tab.url.title": "Von URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Erstellen", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Quelle", + "torrents.create.trackers.label": "Tracker", + "torrents.create.tracker.input.placeholder": "Tracker-URL", + "torrents.create.base.name.label": "Basisname", + "torrents.create.base.name.input.placeholder": "Optionale Basisdatei oder Verzeichnisname des Torrent", + "torrents.create.comment.label": "Kommentar", + "torrents.create.comment.input.placeholder": "Optionaler Kommentar in Torrent-Datei", + "torrents.create.info.source.label": "Informationsquelle", + "torrents.create.info.source.input.placeholder": "Optionaler Quelleintrag in Infohash", + "torrents.create.is.private.label": "Privat", + "torrents.create.tags.input.placeholder": "Tags für Flood. Wird dem erstellten Torrent nicht hinzugefügt.", + "torrents.destination.base_path": "Als Basispfad verwenden", + "torrents.destination.completed": "Abgeschlossen", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Details", + "torrents.details.files": "Dateien", + "torrents.details.files.loading": "Lade Dateidetails...", + "torrents.details.files.download.file": "{count, plural, =1 {Datei herunterladen} other {Dateien herunterladen}}", + "torrents.details.general.comment": "Kommentar", + "torrents.details.general.connected": "{connected} verbunden von {total}", + "torrents.details.general.date.added": "Hinzugefügt", + "torrents.details.general.date.created": "Erstellungszeit", + "torrents.details.general.downloaded": "Heruntergeladen", + "torrents.details.general.free.disk.space": "Freier Speicherplatz", + "torrents.details.general.hash": "Hashwert", + "torrents.details.general.heading.general": "Allgemein", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Übertragungen", + "torrents.details.general.location": "Speicherort", + "torrents.details.general.none": "Keine", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignoriert", + "torrents.details.general.scheduler.obeyed": "Befolgt", + "torrents.details.general.scheduler": "Zeitplaner", + "torrents.details.general.seeds": "Seeds", + "torrents.details.general.size": "Größe", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Tracker-Meldung", + "torrents.details.general.type.private": "Privat", + "torrents.details.general.type.public": "Öffentlich", + "torrents.details.general.type": "Typ", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Es gibt keine Peer-Daten für diesen Torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} Datei ausgewählt} other {{countElement} Dateien ausgewählt}}", + "torrents.details.selected.files.set.priority": "Priorität setzen", + "torrents.details.trackers.no.data": "Es gibt keine Tracker-Daten für diesen Torrent.", + "torrents.details.trackers.type": "Typ", + "torrents.details.trackers": "Tracker", + "torrents.generate.magnet.heading": "Magnet-Link generieren", + "torrents.generate.magnet.loading.trackers": "Lade Tracker...", + "torrents.generate.magnet.private.torrent": "Dies ist ein privates Torrent.", + "torrents.generate.magnet.magnet": "Magnet-Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet-Link mit Trackern", + "torrents.list.clear.filters": "Filter zurücksetzen", + "torrents.list.context.check.hash": "Hash prüfen", + "torrents.list.context.details": "Torrent-Details", + "torrents.list.context.generate.magnet": "Magnet-Link generieren", + "torrents.list.context.move": "Speicherort setzen", + "torrents.list.context.pause": "Pausieren", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "Priorität", + "torrents.list.context.remove": "Entfernen", + "torrents.list.context.set.tags": "Tags setzen", + "torrents.list.context.set.trackers": "Tracker festlegen", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stoppen", + "torrents.list.no.torrents": "Keine anzuzeigenden Torrents.", + "torrents.list.drop": "Dateien hier ablegen, um sie hinzuzufügen.", + "torrents.list.cannot.connect": "Keine Verbindung zum Client möglich.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "von", + "torrents.move.button.set.location": "Speicherort setzen", + "torrents.move.button.state.setting": "Einstellung...", + "torrents.move.data.label": "Daten verschieben", + "torrents.move.check_hash.label": "Hash prüfen", + "torrents.move.heading": "Speicherort des Torrents setzen", + "torrents.properties.date.added": "Hinzugefügt", + "torrents.properties.comment": "Kommentar", + "torrents.properties.creation.date": "Erstellungsdatum", + "torrents.properties.directory": "Speicherort", + "torrents.properties.download.speed": "DL-Geschwindigkeit", + "torrents.properties.download.total": "Heruntergeladen", + "torrents.properties.eta": "Fertig in", + "torrents.properties.free.disk.space": "Freier Speicherplatz", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Terminplaner ignorieren", + "torrents.properties.is.private": "Privat", + "torrents.properties.name": "Name", + "torrents.properties.percentage": "Fortschritt", + "torrents.properties.ratio": "Verhältnis", + "torrents.properties.size": "Größe", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Tracker-Meldung", + "torrents.properties.upload.speed": "UL-Geschwindigkeit", + "torrents.properties.upload.total": "Hochgeladen", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Tracker", + "torrents.remove.are.you.sure": "Wollen Sie wirklich {count, plural,=1 {# Torrent} other{# Torrents}} entfernen?", + "torrents.remove.delete.data": "Daten löschen", + "torrents.remove.error.no.torrents.selected": "Sie haben keine Torrents ausgewählt.", + "torrents.remove": "Torrents entfernen", + "torrents.set.tags.button.set": "Tags zuweisen", + "torrents.set.tags.heading": "Tags zuweisen", + "torrents.set.tags.enter.tags": "Tags eingeben", + "torrents.set.trackers.button.set": "Tracker festlegen", + "torrents.set.trackers.heading": "Tracker festlegen", + "torrents.set.trackers.enter.tracker": "Tracker eingeben", + "torrents.set.trackers.loading.trackers": "Lade Tracker...", + "torrents.sort.title": "Sortieren nach", + "connection-interruption.heading": "Keine Verbindung zum Client möglich", + "status.diskusage.title": "Speichernutzung", + "status.diskusage.used": "Belegt", + "status.diskusage.free": "Frei", + "status.diskusage.total": "Gesamt", + "locale.language.auto": "Automatisch", + "locale.language.translate": "Übersetzung starten", + "dependency.loading.notifications": "Benachrichtigungen", + "dependency.loading.torrent.taxonomy": "Torrent-Taxonomie", + "dependency.loading.transfer.rate.details": "Datenübertragungsrate Details", + "dependency.loading.transfer.history": "Datenübertragungsverlauf", + "dependency.loading.torrent.list": "Torrent-Liste" +} diff --git a/client/src/javascript/i18n/translations/el.json b/client/src/javascript/i18n/translations/el.json new file mode 100644 index 000000000..27901cc9e --- /dev/null +++ b/client/src/javascript/i18n/translations/el.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Add Torrent", + "actionbar.button.remove.torrent": "Remove Torrent", + "alert.torrent.add": "Successfully added {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Failed to add {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Successfully moved {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move.failed": "Failed to move {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Successfully removed {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Failed to remove {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Successfully saved settings.", + "auth.add.user": "Add User", + "auth.create.account": "Create Account", + "auth.create.an.account": "Create an account", + "auth.create.an.account.intro": "Welcome to Flood!", + "auth.current.user": "Current User", + "auth.error.username.empty": "Username cannot be empty.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "Log In", + "auth.login": "Login", + "auth.login.intro": "Log in to your account.", + "auth.password": "Password", + "auth.user.accounts": "User Accounts", + "auth.username": "Username", + "auth.admin": "Admin", + "auth.message.not.admin": "User is not Admin", + "button.add": "Add", + "button.cancel": "Cancel", + "button.close": "Close", + "button.download": "Download", + "button.no": "No", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Save Settings", + "button.save.feed": "Save", + "button.state.adding": "Adding...", + "button.yes": "Yes", + "button.new": "New", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Connection could not be verified.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Connectivity Issue", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "Add Download Rule", + "feeds.add.feed": "Add Feed", + "feeds.applicable.feed": "Applicable Feed", + "feeds.apply.tags": "Apply Tags", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Exclude Pattern", + "feeds.existing.feeds": "Existing Feeds", + "feeds.existing.rules": "Existing Rules", + "feeds.interval": "Interval", + "feeds.label": "Label", + "feeds.match.count": "{count, plural, =1 {# match} other {# matches}}", + "feeds.match.pattern": "Match Pattern", + "feeds.match": "Match", + "feeds.exclude": "Exclude", + "feeds.no.feeds.available": "No feeds available.", + "feeds.no.feeds.defined": "No feeds defined.", + "feeds.no.items.matching": "No items matching search term.", + "feeds.no.rules.defined": "No rules defined.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Select Feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start on load", + "feeds.tabs.download.rules": "Download Rules", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent Feeds", + "feeds.tags": "Tags", + "feeds.test.match": "Test Match Pattern", + "feeds.time.hr": "Hours", + "feeds.time.min": "Minutes", + "feeds.time.day": "Days", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Search term", + "feeds.search.term": "Search term", + "feeds.validation.invalid.regular.expression": "Invalid regular expression.", + "feeds.validation.must.select.feed": "You must select a feed.", + "feeds.validation.must.specify.destination": "You must specify a destination.", + "feeds.validation.must.specify.label": "You must specify a label.", + "feeds.validation.must.specify.valid.feed.url": "You must specify a valid feed URL.", + "feeds.validation.interval.not.positive": "The interval must be a positive integer.", + "feeds.browse.feeds": "Browse feeds", + "filesystem.empty.directory": "Empty directory.", + "filesystem.error.eacces": "Flood does not have permission to read this directory.", + "filesystem.error.enoent": "This path does not exist. It will be created.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "Fetching directory structure...", + "filesystem.parent.directory": "Parent Directory", + "filter.all": "All", + "filter.status.title": "Filter by Status", + "filter.status.downloading": "Downloading", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Complete", + "filter.status.active": "Active", + "filter.status.inactive": "Inactive", + "filter.status.error": "Error", + "filter.status.stopped": "Stopped", + "filter.status.checking": "Checking", + "filter.tracker.title": "Filter by Tracker", + "filter.tag.title": "Filter by Tag", + "filter.untagged": "Untagged", + "general.ago": "ago", + "general.at": "at", + "general.to": "to", + "general.of": "of", + "general.clipboard.copy": "Copy", + "general.clipboard.copied": "Copied", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.", + "mediainfo.fetching": "Fetching...", + "mediainfo.heading": "Mediainfo Output", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Finished Downloading", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Error Reported", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Clear All", + "notification.showing": "Showing", + "priority.dont.download": "Don't Download", + "priority.high": "High", + "priority.low": "Low", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Download Slots Global", + "settings.bandwidth.slots.download.label": "Download Slots Per Torrent", + "settings.bandwidth.slots.heading": "Slot Availability", + "settings.bandwidth.slots.upload.global.label": "Upload Slots Global", + "settings.bandwidth.slots.upload.label": "Upload Slots Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown Presets: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown Presets: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Global Download Rate Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Global Upload Rate Throttle", + "settings.bandwidth.transferrate.heading": "Transfer Rate Throttles", + "settings.connectivity.dht.label": "Enable DHT", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentralized Peer Discovery", + "settings.connectivity.incoming.heading": "Incoming Connections", + "settings.connectivity.ip.hostname.label": "Reported IP/Hostname", + "settings.connectivity.max.http.connections": "Maximum HTTP Connections", + "settings.connectivity.peer.exchange.label": "Enable Peer Exchange", + "settings.connectivity.peers.desired.label": "Peers Desired", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maximum Peers", + "settings.connectivity.peers.min.label": "Minimum Peers", + "settings.connectivity.peers.seeding.max.label": "Maximum Peers Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimum Peers Seeding", + "settings.connectivity.port.open.label": "Open Port", + "settings.connectivity.port.randomize.label": "Randomize Port", + "settings.connectivity.port.range.label": "Port Range", + "settings.resources.disk.check.hash.label": "Verify Hash on Completion", + "settings.resources.disk.download.location.label": "Default Download Directory", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maximum Open Files", + "settings.resources.memory.heading": "Memory", + "settings.resources.memory.max.label": "Max Memory Usage", + "settings.tabs.bandwidth": "Bandwidth", + "settings.tabs.connectivity": "Connectivity", + "settings.tabs.heading": "Settings", + "settings.tabs.resources": "Resources", + "settings.tabs.authentication": "Authentication", + "settings.tabs.userinterface": "User Interface", + "settings.tabs.diskusage": "Disk Usage", + "settings.tabs.about": "About", + "settings.ui.locale": "Locale", + "settings.ui.language": "Language", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent List Display", + "settings.ui.torrent.size": "Torrent Size", + "settings.ui.torrent.size.expanded": "Expanded View", + "settings.ui.torrent.size.condensed": "Condensed View", + "settings.ui.torrent.details.enabled": "Enabled", + "settings.ui.torrent.details.tags.placement": "In the expanded view, tags work best at the end of the list.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Show", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "About Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Settings", + "sidebar.button.speedlimits": "Speed Limits", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log Out", + "sidebar.search.placeholder": "Search torrents", + "sidebar.transferdata.downloaded": "Downloaded", + "sidebar.transferdata.uploaded": "Uploaded", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Unlimited", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "hr", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Add Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Destination", + "torrents.add.destination.placeholder": "Destination", + "torrents.add.heading": "Add Torrents", + "torrents.add.start.label": "Start Torrent", + "torrents.add.tab.file.browse": "or click to browse", + "torrents.add.tab.file.drop": "Drop some files here,", + "torrents.add.tab.file.title": "By File", + "torrents.add.tab.url.input.placeholder": "Torrent URL or Magnet Link", + "torrents.add.tab.url.title": "By URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "Use as Base Path", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Details", + "torrents.details.files": "Files", + "torrents.details.files.loading": "Loading file detail...", + "torrents.details.files.download.file": "{count, plural, =1 {Download File} other {Download Files}}", + "torrents.details.general.comment": "Comment", + "torrents.details.general.connected": "{connected} connected of {total}", + "torrents.details.general.date.added": "Added", + "torrents.details.general.date.created": "Creation Date", + "torrents.details.general.downloaded": "Downloaded", + "torrents.details.general.free.disk.space": "Free Disk Space", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "General", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfer", + "torrents.details.general.location": "Location", + "torrents.details.general.none": "None", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignored", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Scheduler", + "torrents.details.general.seeds": "Seeds", + "torrents.details.general.size": "Size", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Tracker Message", + "torrents.details.general.type.private": "Private", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "There is no peer data for this torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} selected file} other {{countElement} selected files}}", + "torrents.details.selected.files.set.priority": "Set Priority", + "torrents.details.trackers.no.data": "There is no tracker data for this torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Clear Filters", + "torrents.list.context.check.hash": "Check Hash", + "torrents.list.context.details": "Torrent Details", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Set Torrent Location", + "torrents.list.context.pause": "Pause", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "Priority", + "torrents.list.context.remove": "Remove", + "torrents.list.context.set.tags": "Set Tags", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stop", + "torrents.list.no.torrents": "No torrents to display.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "of", + "torrents.move.button.set.location": "Set Location", + "torrents.move.button.state.setting": "Setting...", + "torrents.move.data.label": "Move data", + "torrents.move.check_hash.label": "Check hash", + "torrents.move.heading": "Set Torrent Location", + "torrents.properties.date.added": "Added", + "torrents.properties.comment": "Comment", + "torrents.properties.creation.date": "Creation Date", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Download Speed", + "torrents.properties.download.total": "Downloaded", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Free Disk Space", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignore Scheduler", + "torrents.properties.is.private": "Private", + "torrents.properties.name": "Name", + "torrents.properties.percentage": "Percent Complete", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "File Size", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Tracker Message", + "torrents.properties.upload.speed": "Upload Speed", + "torrents.properties.upload.total": "Uploaded", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Are you sure you want to remove {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Delete data", + "torrents.remove.error.no.torrents.selected": "You haven't selected any torrents.", + "torrents.remove": "Remove Torrents", + "torrents.set.tags.button.set": "Set Tags", + "torrents.set.tags.heading": "Set Tags", + "torrents.set.tags.enter.tags": "Enter tags", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "Sort By", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Disk Usage", + "status.diskusage.used": "Used", + "status.diskusage.free": "Free", + "status.diskusage.total": "Total", + "locale.language.auto": "Automatic", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Details", + "dependency.loading.transfer.history": "Data Transfer History", + "dependency.loading.torrent.list": "Torrent List" +} diff --git a/client/src/javascript/i18n/translations/en.json b/client/src/javascript/i18n/translations/en.json new file mode 100644 index 000000000..bb96c3045 --- /dev/null +++ b/client/src/javascript/i18n/translations/en.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "crwdns2898:0crwdne2898:0", + "actionbar.button.stop.torrent": "crwdns2900:0crwdne2900:0", + "actionbar.button.add.torrent": "crwdns2902:0crwdne2902:0", + "actionbar.button.remove.torrent": "crwdns2904:0crwdne2904:0", + "alert.torrent.add": "crwdns2906:0countElement={countElement}crwdnd2906:0count={count}crwdne2906:0", + "alert.torrent.add.failed": "crwdns2908:0countElement={countElement}crwdnd2908:0count={count}crwdne2908:0", + "alert.torrent.move": "crwdns2910:0countElement={countElement}crwdnd2910:0count={count}crwdne2910:0", + "alert.torrent.move.failed": "crwdns2912:0countElement={countElement}crwdnd2912:0count={count}crwdne2912:0", + "alert.torrent.remove": "crwdns2914:0countElement={countElement}crwdnd2914:0count={count}crwdne2914:0", + "alert.torrent.remove.failed": "crwdns2916:0countElement={countElement}crwdnd2916:0count={count}crwdne2916:0", + "alert.settings.saved": "crwdns2918:0crwdne2918:0", + "auth.add.user": "crwdns2920:0crwdne2920:0", + "auth.create.account": "crwdns2928:0crwdne2928:0", + "auth.create.an.account": "crwdns2930:0crwdne2930:0", + "auth.create.an.account.intro": "crwdns2932:0crwdne2932:0", + "auth.current.user": "crwdns2934:0crwdne2934:0", + "auth.error.username.empty": "crwdns2936:0crwdne2936:0", + "auth.error.password.empty": "crwdns3548:0crwdne3548:0", + "auth.input.clear": "crwdns3648:0crwdne3648:0", + "auth.log.in": "crwdns2938:0crwdne2938:0", + "auth.login": "crwdns2940:0crwdne2940:0", + "auth.login.intro": "crwdns2942:0crwdne2942:0", + "auth.password": "crwdns2944:0crwdne2944:0", + "auth.user.accounts": "crwdns2946:0crwdne2946:0", + "auth.username": "crwdns2948:0crwdne2948:0", + "auth.admin": "crwdns2950:0crwdne2950:0", + "auth.message.not.admin": "crwdns2952:0crwdne2952:0", + "button.add": "crwdns2962:0crwdne2962:0", + "button.cancel": "crwdns2964:0crwdne2964:0", + "button.close": "crwdns3692:0crwdne3692:0", + "button.download": "crwdns3520:0crwdne3520:0", + "button.no": "crwdns2966:0crwdne2966:0", + "button.ok": "crwdns3680:0crwdne3680:0", + "button.retry": "crwdns3654:0crwdne3654:0", + "button.save": "crwdns2968:0crwdne2968:0", + "button.save.feed": "crwdns2970:0crwdne2970:0", + "button.state.adding": "crwdns2974:0crwdne2974:0", + "button.yes": "crwdns2976:0crwdne2976:0", + "button.new": "crwdns2978:0crwdne2978:0", + "connection-interruption.action.selection.retry": "crwdns3656:0crwdne3656:0", + "connection-interruption.action.selection.config": "crwdns3658:0crwdne3658:0", + "connection-interruption.not.admin": "crwdns3660:0crwdne3660:0", + "connection-interruption.verification-error": "crwdns3492:0crwdne3492:0", + "connection.settings.client.select": "crwdns3550:0crwdne3550:0", + "connection.settings.error.empty": "crwdns3552:0crwdne3552:0", + "connection.settings.rtorrent": "crwdns3554:0crwdne3554:0", + "connection.settings.rtorrent.type": "crwdns3556:0crwdne3556:0", + "connection.settings.rtorrent.type.tcp": "crwdns3558:0crwdne3558:0", + "connection.settings.rtorrent.type.tcp.warning": "crwdns3652:0crwdne3652:0", + "connection.settings.rtorrent.type.socket": "crwdns3560:0crwdne3560:0", + "connection.settings.rtorrent.host": "crwdns3562:0crwdne3562:0", + "connection.settings.rtorrent.host.input.placeholder": "crwdns3564:0crwdne3564:0", + "connection.settings.rtorrent.port": "crwdns3566:0crwdne3566:0", + "connection.settings.rtorrent.port.input.placeholder": "crwdns3568:0crwdne3568:0", + "connection.settings.rtorrent.socket": "crwdns3570:0crwdne3570:0", + "connection.settings.rtorrent.socket.input.placeholder": "crwdns3572:0crwdne3572:0", + "connection.settings.qbittorrent": "crwdns3630:0crwdne3630:0", + "connection.settings.qbittorrent.url": "crwdns3632:0crwdne3632:0", + "connection.settings.qbittorrent.url.input.placeholder": "crwdns3634:0crwdne3634:0", + "connection.settings.qbittorrent.username": "crwdns3636:0crwdne3636:0", + "connection.settings.qbittorrent.username.input.placeholder": "crwdns3638:0crwdne3638:0", + "connection.settings.qbittorrent.password": "crwdns3640:0crwdne3640:0", + "connection.settings.qbittorrent.password.input.placeholder": "crwdns3642:0crwdne3642:0", + "connection.settings.transmission": "crwdns3666:0crwdne3666:0", + "connection.settings.transmission.url": "crwdns3668:0crwdne3668:0", + "connection.settings.transmission.url.input.placeholder": "crwdns3670:0crwdne3670:0", + "connection.settings.transmission.username": "crwdns3672:0crwdne3672:0", + "connection.settings.transmission.username.input.placeholder": "crwdns3674:0crwdne3674:0", + "connection.settings.transmission.password": "crwdns3676:0crwdne3676:0", + "connection.settings.transmission.password.input.placeholder": "crwdns3678:0crwdne3678:0", + "connectivity.modal.title": "crwdns2980:0crwdne2980:0", + "connectivity.modal.content": "crwdns3574:0crwdne3574:0", + "feeds.add.automatic.download.rule": "crwdns2984:0crwdne2984:0", + "feeds.add.feed": "crwdns2986:0crwdne2986:0", + "feeds.applicable.feed": "crwdns2988:0crwdne2988:0", + "feeds.apply.tags": "crwdns2990:0crwdne2990:0", + "feeds.check": "crwdns3650:0crwdne3650:0", + "feeds.exclude.pattern": "crwdns2992:0crwdne2992:0", + "feeds.existing.feeds": "crwdns2994:0crwdne2994:0", + "feeds.existing.rules": "crwdns2996:0crwdne2996:0", + "feeds.interval": "crwdns3524:0crwdne3524:0", + "feeds.label": "crwdns2998:0crwdne2998:0", + "feeds.match.count": "crwdns3000:0count={count}crwdne3000:0", + "feeds.match.pattern": "crwdns3002:0crwdne3002:0", + "feeds.match": "crwdns3004:0crwdne3004:0", + "feeds.exclude": "crwdns3006:0crwdne3006:0", + "feeds.no.feeds.available": "crwdns3008:0crwdne3008:0", + "feeds.no.feeds.defined": "crwdns3010:0crwdne3010:0", + "feeds.no.items.matching": "crwdns3526:0crwdne3526:0", + "feeds.no.rules.defined": "crwdns3012:0crwdne3012:0", + "feeds.regEx": "crwdns3014:0crwdne3014:0", + "feeds.select.feed": "crwdns3016:0crwdne3016:0", + "feeds.select.interval": "crwdns3018:0crwdne3018:0", + "feeds.start.on.load": "crwdns3020:0crwdne3020:0", + "feeds.tabs.download.rules": "crwdns3022:0crwdne3022:0", + "feeds.tabs.feeds": "crwdns3024:0crwdne3024:0", + "feeds.tabs.heading": "crwdns3026:0crwdne3026:0", + "feeds.tags": "crwdns3028:0crwdne3028:0", + "feeds.test.match": "crwdns3528:0crwdne3528:0", + "feeds.time.hr": "crwdns3030:0crwdne3030:0", + "feeds.time.min": "crwdns3032:0crwdne3032:0", + "feeds.time.day": "crwdns3034:0crwdne3034:0", + "feeds.torrent.destination": "crwdns3036:0crwdne3036:0", + "feeds.url": "crwdns3038:0crwdne3038:0", + "feeds.search": "crwdns3040:0crwdne3040:0", + "feeds.search.term": "crwdns3530:0crwdne3530:0", + "feeds.validation.invalid.regular.expression": "crwdns3042:0crwdne3042:0", + "feeds.validation.must.select.feed": "crwdns3044:0crwdne3044:0", + "feeds.validation.must.specify.destination": "crwdns3046:0crwdne3046:0", + "feeds.validation.must.specify.label": "crwdns3048:0crwdne3048:0", + "feeds.validation.must.specify.valid.feed.url": "crwdns3050:0crwdne3050:0", + "feeds.validation.interval.not.positive": "crwdns3052:0crwdne3052:0", + "feeds.browse.feeds": "crwdns3054:0crwdne3054:0", + "filesystem.empty.directory": "crwdns3056:0crwdne3056:0", + "filesystem.error.eacces": "crwdns3058:0crwdne3058:0", + "filesystem.error.enoent": "crwdns3060:0crwdne3060:0", + "filesystem.error.unknown": "crwdns3606:0crwdne3606:0", + "filesystem.fetching": "crwdns3062:0crwdne3062:0", + "filesystem.parent.directory": "crwdns3518:0crwdne3518:0", + "filter.all": "crwdns3064:0crwdne3064:0", + "filter.status.title": "crwdns3066:0crwdne3066:0", + "filter.status.downloading": "crwdns3068:0crwdne3068:0", + "filter.status.seeding": "crwdns3546:0crwdne3546:0", + "filter.status.completed": "crwdns3070:0crwdne3070:0", + "filter.status.active": "crwdns3072:0crwdne3072:0", + "filter.status.inactive": "crwdns3074:0crwdne3074:0", + "filter.status.error": "crwdns3076:0crwdne3076:0", + "filter.status.stopped": "crwdns3078:0crwdne3078:0", + "filter.status.checking": "crwdns3080:0crwdne3080:0", + "filter.tracker.title": "crwdns3082:0crwdne3082:0", + "filter.tag.title": "crwdns3084:0crwdne3084:0", + "filter.untagged": "crwdns3086:0crwdne3086:0", + "general.ago": "crwdns3088:0crwdne3088:0", + "general.at": "crwdns3090:0crwdne3090:0", + "general.to": "crwdns3092:0crwdne3092:0", + "general.of": "crwdns3094:0crwdne3094:0", + "general.clipboard.copy": "crwdns3096:0crwdne3096:0", + "general.clipboard.copied": "crwdns3098:0crwdne3098:0", + "general.error.unknown": "crwdns3614:0crwdne3614:0", + "mediainfo.execError": "crwdns3100:0crwdne3100:0", + "mediainfo.fetching": "crwdns3102:0crwdne3102:0", + "mediainfo.heading": "crwdns3104:0crwdne3104:0", + "notification.feed.torrent.added.heading": "crwdns3616:0crwdne3616:0", + "notification.feed.torrent.added.body": "crwdns3618:0{title}crwdne3618:0", + "notification.no.notification": "crwdns3662:0crwdne3662:0", + "notification.torrent.finished.heading": "crwdns3106:0crwdne3106:0", + "notification.torrent.finished.body": "crwdns3108:0{name}crwdne3108:0", + "notification.torrent.errored.heading": "crwdns3110:0crwdne3110:0", + "notification.torrent.errored.body": "crwdns3112:0{name}crwdne3112:0", + "notification.clear.all": "crwdns3114:0crwdne3114:0", + "notification.showing": "crwdns3116:0crwdne3116:0", + "priority.dont.download": "crwdns3118:0crwdne3118:0", + "priority.high": "crwdns3120:0crwdne3120:0", + "priority.low": "crwdns3122:0crwdne3122:0", + "priority.normal": "crwdns3124:0crwdne3124:0", + "settings.bandwidth.slots.download.global.label": "crwdns3128:0crwdne3128:0", + "settings.bandwidth.slots.download.label": "crwdns3130:0crwdne3130:0", + "settings.bandwidth.slots.heading": "crwdns3132:0crwdne3132:0", + "settings.bandwidth.slots.upload.global.label": "crwdns3136:0crwdne3136:0", + "settings.bandwidth.slots.upload.label": "crwdns3138:0crwdne3138:0", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "crwdns3140:0crwdne3140:0", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "crwdns3142:0crwdne3142:0", + "settings.bandwidth.transferrate.global.throttle.download": "crwdns3144:0crwdne3144:0", + "settings.bandwidth.transferrate.global.throttle.upload": "crwdns3146:0crwdne3146:0", + "settings.bandwidth.transferrate.heading": "crwdns3148:0crwdne3148:0", + "settings.connectivity.dht.label": "crwdns3150:0crwdne3150:0", + "settings.connectivity.dht.port.label": "crwdns3152:0crwdne3152:0", + "settings.connectivity.dpd.heading": "crwdns3154:0crwdne3154:0", + "settings.connectivity.incoming.heading": "crwdns3156:0crwdne3156:0", + "settings.connectivity.ip.hostname.label": "crwdns3158:0crwdne3158:0", + "settings.connectivity.max.http.connections": "crwdns3160:0crwdne3160:0", + "settings.connectivity.peer.exchange.label": "crwdns3162:0crwdne3162:0", + "settings.connectivity.peers.desired.label": "crwdns3164:0crwdne3164:0", + "settings.connectivity.peers.heading": "crwdns3166:0crwdne3166:0", + "settings.connectivity.peers.max.label": "crwdns3168:0crwdne3168:0", + "settings.connectivity.peers.min.label": "crwdns3170:0crwdne3170:0", + "settings.connectivity.peers.seeding.max.label": "crwdns3172:0crwdne3172:0", + "settings.connectivity.peers.seeding.min.label": "crwdns3174:0crwdne3174:0", + "settings.connectivity.port.open.label": "crwdns3176:0crwdne3176:0", + "settings.connectivity.port.randomize.label": "crwdns3178:0crwdne3178:0", + "settings.connectivity.port.range.label": "crwdns3180:0crwdne3180:0", + "settings.resources.disk.check.hash.label": "crwdns3182:0crwdne3182:0", + "settings.resources.disk.download.location.label": "crwdns3184:0crwdne3184:0", + "settings.resources.disk.heading": "crwdns3186:0crwdne3186:0", + "settings.resources.max.open.files": "crwdns3188:0crwdne3188:0", + "settings.resources.memory.heading": "crwdns3190:0crwdne3190:0", + "settings.resources.memory.max.label": "crwdns3192:0crwdne3192:0", + "settings.tabs.bandwidth": "crwdns3194:0crwdne3194:0", + "settings.tabs.connectivity": "crwdns3196:0crwdne3196:0", + "settings.tabs.heading": "crwdns3198:0crwdne3198:0", + "settings.tabs.resources": "crwdns3200:0crwdne3200:0", + "settings.tabs.authentication": "crwdns3202:0crwdne3202:0", + "settings.tabs.userinterface": "crwdns3204:0crwdne3204:0", + "settings.tabs.diskusage": "crwdns3206:0crwdne3206:0", + "settings.tabs.about": "crwdns3208:0crwdne3208:0", + "settings.ui.locale": "crwdns3210:0crwdne3210:0", + "settings.ui.language": "crwdns3212:0crwdne3212:0", + "settings.ui.tag.selector.mode": "crwdns3682:0crwdne3682:0", + "settings.ui.tag.selector.mode.single": "crwdns3684:0crwdne3684:0", + "settings.ui.tag.selector.mode.multi": "crwdns3686:0crwdne3686:0", + "settings.ui.torrent.list": "crwdns3214:0crwdne3214:0", + "settings.ui.torrent.size": "crwdns3216:0crwdne3216:0", + "settings.ui.torrent.size.expanded": "crwdns3218:0crwdne3218:0", + "settings.ui.torrent.size.condensed": "crwdns3220:0crwdne3220:0", + "settings.ui.torrent.details.enabled": "crwdns3222:0crwdne3222:0", + "settings.ui.torrent.details.tags.placement": "crwdns3224:0crwdne3224:0", + "settings.ui.torrent.context.menu.items.show": "crwdns3532:0crwdne3532:0", + "settings.ui.displayed.details": "crwdns3226:0crwdne3226:0", + "settings.ui.displayed.context.menu.items": "crwdns3534:0crwdne3534:0", + "settings.diskusage.show": "crwdns3228:0crwdne3228:0", + "settings.diskusage.mount.points": "crwdns3230:0crwdne3230:0", + "settings.about.flood": "crwdns3232:0crwdne3232:0", + "sidebar.button.feeds": "crwdns3234:0crwdne3234:0", + "sidebar.button.settings": "crwdns3238:0crwdne3238:0", + "sidebar.button.speedlimits": "crwdns3240:0crwdne3240:0", + "sidebar.button.theme.dark": "crwdns3688:0crwdne3688:0", + "sidebar.button.theme.light": "crwdns3690:0crwdne3690:0", + "sidebar.button.log.out": "crwdns3242:0crwdne3242:0", + "sidebar.search.placeholder": "crwdns3244:0crwdne3244:0", + "sidebar.transferdata.downloaded": "crwdns3246:0crwdne3246:0", + "sidebar.transferdata.uploaded": "crwdns3248:0crwdne3248:0", + "sidebar.speedlimits.download": "crwdns3250:0crwdne3250:0", + "sidebar.speedlimits.upload": "crwdns3252:0crwdne3252:0", + "speed.unlimited": "crwdns3254:0crwdne3254:0", + "unit.size.byte": "crwdns3256:0crwdne3256:0", + "unit.size.kilobyte": "crwdns3258:0crwdne3258:0", + "unit.size.megabyte": "crwdns3260:0crwdne3260:0", + "unit.size.gigabyte": "crwdns3262:0crwdne3262:0", + "unit.size.terabyte": "crwdns3264:0crwdne3264:0", + "unit.speed": "crwdns3266:0{baseUnit}crwdne3266:0", + "unit.time.year": "crwdns3268:0crwdne3268:0", + "unit.time.week": "crwdns3270:0crwdne3270:0", + "unit.time.day": "crwdns3272:0crwdne3272:0", + "unit.time.hour": "crwdns3274:0crwdne3274:0", + "unit.time.minute": "crwdns3276:0crwdne3276:0", + "unit.time.second": "crwdns3278:0crwdne3278:0", + "unit.time.infinity": "crwdns3280:0crwdne3280:0", + "torrents.add.button.add": "crwdns3282:0crwdne3282:0", + "torrents.add.cookies.label": "crwdns3608:0crwdne3608:0", + "torrents.add.cookies.input.placeholder": "crwdns3610:0crwdne3610:0", + "torrents.add.destination.label": "crwdns3284:0crwdne3284:0", + "torrents.add.destination.placeholder": "crwdns3286:0crwdne3286:0", + "torrents.add.heading": "crwdns3288:0crwdne3288:0", + "torrents.add.start.label": "crwdns3290:0crwdne3290:0", + "torrents.add.tab.file.browse": "crwdns3292:0crwdne3292:0", + "torrents.add.tab.file.drop": "crwdns3294:0crwdne3294:0", + "torrents.add.tab.file.title": "crwdns3296:0crwdne3296:0", + "torrents.add.tab.url.input.placeholder": "crwdns3298:0crwdne3298:0", + "torrents.add.tab.url.title": "crwdns3300:0crwdne3300:0", + "torrents.add.tab.url.register.magnet.handler": "crwdns3706:0crwdne3706:0", + "torrents.add.tab.create.title": "crwdns3582:0crwdne3582:0", + "torrents.add.torrents.label": "crwdns3302:0crwdne3302:0", + "torrents.add.tags": "crwdns3304:0crwdne3304:0", + "torrents.create.source.path.label": "crwdns3584:0crwdne3584:0", + "torrents.create.trackers.label": "crwdns3586:0crwdne3586:0", + "torrents.create.tracker.input.placeholder": "crwdns3588:0crwdne3588:0", + "torrents.create.base.name.label": "crwdns3590:0crwdne3590:0", + "torrents.create.base.name.input.placeholder": "crwdns3592:0crwdne3592:0", + "torrents.create.comment.label": "crwdns3594:0crwdne3594:0", + "torrents.create.comment.input.placeholder": "crwdns3596:0crwdne3596:0", + "torrents.create.info.source.label": "crwdns3598:0crwdne3598:0", + "torrents.create.info.source.input.placeholder": "crwdns3600:0crwdne3600:0", + "torrents.create.is.private.label": "crwdns3602:0crwdne3602:0", + "torrents.create.tags.input.placeholder": "crwdns3604:0crwdne3604:0", + "torrents.destination.base_path": "crwdns3306:0crwdne3306:0", + "torrents.destination.completed": "crwdns3612:0crwdne3612:0", + "torrents.details.actions.pause": "crwdns3308:0crwdne3308:0", + "torrents.details.actions.start": "crwdns3310:0crwdne3310:0", + "torrents.details.actions.stop": "crwdns3312:0crwdne3312:0", + "torrents.details.details": "crwdns3314:0crwdne3314:0", + "torrents.details.files": "crwdns3316:0crwdne3316:0", + "torrents.details.files.loading": "crwdns3318:0crwdne3318:0", + "torrents.details.files.download.file": "crwdns3320:0count={count}crwdne3320:0", + "torrents.details.general.comment": "crwdns3322:0crwdne3322:0", + "torrents.details.general.connected": "crwdns3324:0{connected}crwdnd3324:0{total}crwdne3324:0", + "torrents.details.general.date.added": "crwdns3326:0crwdne3326:0", + "torrents.details.general.date.created": "crwdns3328:0crwdne3328:0", + "torrents.details.general.downloaded": "crwdns3330:0crwdne3330:0", + "torrents.details.general.free.disk.space": "crwdns3332:0crwdne3332:0", + "torrents.details.general.hash": "crwdns3334:0crwdne3334:0", + "torrents.details.general.heading.general": "crwdns3336:0crwdne3336:0", + "torrents.details.general.heading.torrent": "crwdns3338:0crwdne3338:0", + "torrents.details.general.heading.tracker": "crwdns3340:0crwdne3340:0", + "torrents.details.general.heading.transfer": "crwdns3342:0crwdne3342:0", + "torrents.details.general.location": "crwdns3344:0crwdne3344:0", + "torrents.details.general.none": "crwdns3346:0crwdne3346:0", + "torrents.details.general.peers": "crwdns3348:0crwdne3348:0", + "torrents.details.general.scheduler.ignored": "crwdns3350:0crwdne3350:0", + "torrents.details.general.scheduler.obeyed": "crwdns3352:0crwdne3352:0", + "torrents.details.general.scheduler": "crwdns3354:0crwdne3354:0", + "torrents.details.general.seeds": "crwdns3356:0crwdne3356:0", + "torrents.details.general.size": "crwdns3358:0crwdne3358:0", + "torrents.details.general.tags": "crwdns3360:0crwdne3360:0", + "torrents.details.general.tracker.message": "crwdns3362:0crwdne3362:0", + "torrents.details.general.type.private": "crwdns3364:0crwdne3364:0", + "torrents.details.general.type.public": "crwdns3366:0crwdne3366:0", + "torrents.details.general.type": "crwdns3368:0crwdne3368:0", + "torrents.details.mediainfo": "crwdns3370:0crwdne3370:0", + "torrents.details.peers.no.data": "crwdns3372:0crwdne3372:0", + "torrents.details.peers": "crwdns3374:0crwdne3374:0", + "torrents.details.selected.files": "crwdns3376:0count={count}crwdnd3376:0countElement={countElement}crwdnd3376:0countElement={countElement}crwdne3376:0", + "torrents.details.selected.files.set.priority": "crwdns3378:0crwdne3378:0", + "torrents.details.trackers.no.data": "crwdns3380:0crwdne3380:0", + "torrents.details.trackers.type": "crwdns3382:0crwdne3382:0", + "torrents.details.trackers": "crwdns3384:0crwdne3384:0", + "torrents.generate.magnet.heading": "crwdns3694:0crwdne3694:0", + "torrents.generate.magnet.loading.trackers": "crwdns3696:0crwdne3696:0", + "torrents.generate.magnet.private.torrent": "crwdns3698:0crwdne3698:0", + "torrents.generate.magnet.magnet": "crwdns3700:0crwdne3700:0", + "torrents.generate.magnet.magnet.with.trackers": "crwdns3702:0crwdne3702:0", + "torrents.list.clear.filters": "crwdns3386:0crwdne3386:0", + "torrents.list.context.check.hash": "crwdns3388:0crwdne3388:0", + "torrents.list.context.details": "crwdns3390:0crwdne3390:0", + "torrents.list.context.generate.magnet": "crwdns3704:0crwdne3704:0", + "torrents.list.context.move": "crwdns3392:0crwdne3392:0", + "torrents.list.context.pause": "crwdns3394:0crwdne3394:0", + "torrents.list.context.download": "crwdns3396:0crwdne3396:0", + "torrents.list.context.priority": "crwdns3398:0crwdne3398:0", + "torrents.list.context.remove": "crwdns3400:0crwdne3400:0", + "torrents.list.context.set.tags": "crwdns3402:0crwdne3402:0", + "torrents.list.context.set.trackers": "crwdns3620:0crwdne3620:0", + "torrents.list.context.start": "crwdns3404:0crwdne3404:0", + "torrents.list.context.stop": "crwdns3406:0crwdne3406:0", + "torrents.list.no.torrents": "crwdns3408:0crwdne3408:0", + "torrents.list.drop": "crwdns3576:0crwdne3576:0", + "torrents.list.cannot.connect": "crwdns3578:0crwdne3578:0", + "torrent.list.peers": "crwdns3414:0{connected}crwdnd3414:0{of}crwdnd3414:0{total}crwdne3414:0", + "torrent.list.peers.of": "crwdns3416:0crwdne3416:0", + "torrents.move.button.set.location": "crwdns3418:0crwdne3418:0", + "torrents.move.button.state.setting": "crwdns3420:0crwdne3420:0", + "torrents.move.data.label": "crwdns3422:0crwdne3422:0", + "torrents.move.check_hash.label": "crwdns3424:0crwdne3424:0", + "torrents.move.heading": "crwdns3426:0crwdne3426:0", + "torrents.properties.date.added": "crwdns3428:0crwdne3428:0", + "torrents.properties.comment": "crwdns3432:0crwdne3432:0", + "torrents.properties.creation.date": "crwdns3434:0crwdne3434:0", + "torrents.properties.directory": "crwdns3664:0crwdne3664:0", + "torrents.properties.download.speed": "crwdns3436:0crwdne3436:0", + "torrents.properties.download.total": "crwdns3438:0crwdne3438:0", + "torrents.properties.eta": "crwdns3440:0crwdne3440:0", + "torrents.properties.free.disk.space": "crwdns3442:0crwdne3442:0", + "torrents.properties.hash": "crwdns3444:0crwdne3444:0", + "torrents.properties.ignore.schedule": "crwdns3446:0crwdne3446:0", + "torrents.properties.is.private": "crwdns3448:0crwdne3448:0", + "torrents.properties.name": "crwdns3450:0crwdne3450:0", + "torrents.properties.percentage": "crwdns3452:0crwdne3452:0", + "torrents.properties.ratio": "crwdns3454:0crwdne3454:0", + "torrents.properties.size": "crwdns3456:0crwdne3456:0", + "torrents.properties.tags": "crwdns3458:0crwdne3458:0", + "torrents.properties.tracker.message": "crwdns3460:0crwdne3460:0", + "torrents.properties.upload.speed": "crwdns3462:0crwdne3462:0", + "torrents.properties.upload.total": "crwdns3464:0crwdne3464:0", + "torrents.properties.seeds": "crwdns3466:0crwdne3466:0", + "torrents.properties.peers": "crwdns3468:0crwdne3468:0", + "torrents.properties.trackers": "crwdns3470:0crwdne3470:0", + "torrents.remove.are.you.sure": "crwdns3472:0count={count}crwdne3472:0", + "torrents.remove.delete.data": "crwdns3474:0crwdne3474:0", + "torrents.remove.error.no.torrents.selected": "crwdns3476:0crwdne3476:0", + "torrents.remove": "crwdns3478:0crwdne3478:0", + "torrents.set.tags.button.set": "crwdns3480:0crwdne3480:0", + "torrents.set.tags.heading": "crwdns3482:0crwdne3482:0", + "torrents.set.tags.enter.tags": "crwdns3484:0crwdne3484:0", + "torrents.set.trackers.button.set": "crwdns3622:0crwdne3622:0", + "torrents.set.trackers.heading": "crwdns3624:0crwdne3624:0", + "torrents.set.trackers.enter.tracker": "crwdns3626:0crwdne3626:0", + "torrents.set.trackers.loading.trackers": "crwdns3628:0crwdne3628:0", + "torrents.sort.title": "crwdns3486:0crwdne3486:0", + "connection-interruption.heading": "crwdns3580:0crwdne3580:0", + "status.diskusage.title": "crwdns3496:0crwdne3496:0", + "status.diskusage.used": "crwdns3512:0crwdne3512:0", + "status.diskusage.free": "crwdns3514:0crwdne3514:0", + "status.diskusage.total": "crwdns3516:0crwdne3516:0", + "locale.language.auto": "crwdns3498:0crwdne3498:0", + "locale.language.translate": "crwdns3646:0crwdne3646:0", + "dependency.loading.notifications": "crwdns3502:0crwdne3502:0", + "dependency.loading.torrent.taxonomy": "crwdns3504:0crwdne3504:0", + "dependency.loading.transfer.rate.details": "crwdns3506:0crwdne3506:0", + "dependency.loading.transfer.history": "crwdns3508:0crwdne3508:0", + "dependency.loading.torrent.list": "crwdns3510:0crwdne3510:0" +} diff --git a/client/src/javascript/i18n/translations/es.json b/client/src/javascript/i18n/translations/es.json new file mode 100644 index 000000000..077bdddfe --- /dev/null +++ b/client/src/javascript/i18n/translations/es.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Iniciar Torrent", + "actionbar.button.stop.torrent": "Detener Torrent", + "actionbar.button.add.torrent": "Añadir Torrent", + "actionbar.button.remove.torrent": "Eliminar Torrent", + "alert.torrent.add": "{countElement} {count, plural, =1 {torrent agregado} other {torrents agregados}}.", + "alert.torrent.add.failed": "{countElement} {count, plural, =1 {torrent fallo al agregar} other {torrents fallaron al agregarse}}.", + "alert.torrent.move": "{countElement} {count, plural, =1 {torrent se ha movido} other {torrents se han movido}}.", + "alert.torrent.move.failed": "{countElement} {count, plural, =1 {torrent fallo al moverse} other {torrents fallaron al moverse}}.", + "alert.torrent.remove": "{countElement} {count, plural, =1 {torrent se ha eliminado} other {torrents se han eliminado}}.", + "alert.torrent.remove.failed": "{countElement} {count, plural, =1 {torrent fallo al eliminarse} other {torrents fallaron al eliminarse}}.", + "alert.settings.saved": "Configuración Guardada.", + "auth.add.user": "Agregar usuario", + "auth.create.account": "Crear Cuenta", + "auth.create.an.account": "Crear una Cuenta", + "auth.create.an.account.intro": "¡Bienvenido a Flood!", + "auth.current.user": "Usuario actual", + "auth.error.username.empty": "Nombre de usuario no puede estar en blanco.", + "auth.error.password.empty": "La contraseña no puede estar vacía.", + "auth.input.clear": "Limpiar", + "auth.log.in": "Iniciar Sesión", + "auth.login": "Acceder", + "auth.login.intro": "Inicie sesión en su cuenta.", + "auth.password": "Contraseña", + "auth.user.accounts": "Cuentas de Usuario", + "auth.username": "Nombre de Usuario", + "auth.admin": "Admin", + "auth.message.not.admin": "El Usuario no es Administrador", + "button.add": "Agregar", + "button.cancel": "Cancelar", + "button.close": "Close", + "button.download": "Descargar", + "button.no": "Nu", + "button.ok": "OK", + "button.retry": "Reintenta", + "button.save": "Guardar Configuración", + "button.save.feed": "Guardar", + "button.state.adding": "Agregando...", + "button.yes": "Sí", + "button.new": "Nuevo", + "connection-interruption.action.selection.retry": "Reintentar con la configuración de conexión actual del cliente", + "connection-interruption.action.selection.config": "Actualizar configuración de conexión de cliente", + "connection-interruption.not.admin": "Por favor contacte a su administrador de Flood si esto continúa.", + "connection-interruption.verification-error": "No se pudo verificar la conexión.", + "connection.settings.client.select": "Cliente", + "connection.settings.error.empty": "Los ajustes de conexión no pueden estar vacíos.", + "connection.settings.rtorrent": "rótula", + "connection.settings.rtorrent.type": "Tipo de conexión", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exponer rTorrent vía TCP puede permitir la escalada de privilegios.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Anfitrión", + "connection.settings.rtorrent.host.input.placeholder": "Nombre de host o IP", + "connection.settings.rtorrent.port": "Puerto", + "connection.settings.rtorrent.port.input.placeholder": "Puerto", + "connection.settings.rtorrent.socket": "Ruta", + "connection.settings.rtorrent.socket.input.placeholder": "Ruta al socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL a la API web qBittorrent", + "connection.settings.qbittorrent.username": "Usuario", + "connection.settings.qbittorrent.username.input.placeholder": "Usuario", + "connection.settings.qbittorrent.password": "Contraseña", + "connection.settings.qbittorrent.password.input.placeholder": "Contraseña", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL a la interfaz RPC de Transmission", + "connection.settings.transmission.username": "Nombre de Usuario", + "connection.settings.transmission.username.input.placeholder": "Nombre de Usuario", + "connection.settings.transmission.password": "Contraseña", + "connection.settings.transmission.password.input.placeholder": "Contraseña", + "connectivity.modal.title": "Problema de conectividad", + "connectivity.modal.content": "No se puede conectar al cliente. Por favor, actualice la configuración de conexión.", + "feeds.add.automatic.download.rule": "Agregar Regla de Descarga", + "feeds.add.feed": "Agregar Fuente", + "feeds.applicable.feed": "Fuente Correspondiente", + "feeds.apply.tags": "Aplicar Etiquetas", + "feeds.check": "Validar la regla probándola. No guardado o enviado.", + "feeds.exclude.pattern": "Patrón de Exclusión", + "feeds.existing.feeds": "Fuentes Existentes", + "feeds.existing.rules": "Reglas Existentes", + "feeds.interval": "Intervalo", + "feeds.label": "Rótulo", + "feeds.match.count": "{count, plural, =1 {# Encontrado} other {# Encontrados}}", + "feeds.match.pattern": "Patrón de Inclusión", + "feeds.match": "Encontrado", + "feeds.exclude": "Excluir", + "feeds.no.feeds.available": "No hay fuentes disponibles", + "feeds.no.feeds.defined": "No hay fuentes definidas", + "feeds.no.items.matching": "No hay elementos que coincidan con el término de búsqueda.", + "feeds.no.rules.defined": "No hay reglas definidas", + "feeds.regEx": "Regular", + "feeds.select.feed": "Seleccionar Fuente", + "feeds.select.interval": "Intervalo", + "feeds.start.on.load": "Iniciar al Cargar", + "feeds.tabs.download.rules": "Reglas de Descarga", + "feeds.tabs.feeds": "Fuentes", + "feeds.tabs.heading": "Fuentes de Torrent", + "feeds.tags": "Etiquetas", + "feeds.test.match": "Prueba de Patrón de Coincidencia", + "feeds.time.hr": "{durationValue} hr", + "feeds.time.min": "{durationValue} min", + "feeds.time.day": "Días", + "feeds.torrent.destination": "Destinación de Torrent", + "feeds.url": "URL", + "feeds.search": "Buscar término", + "feeds.search.term": "Buscar término", + "feeds.validation.invalid.regular.expression": "RegEx invalida.", + "feeds.validation.must.select.feed": "Debe seleccionar una fuente", + "feeds.validation.must.specify.destination": "Debe seleccionar una destinación.", + "feeds.validation.must.specify.label": "Debe seleccionar un rotulo", + "feeds.validation.must.specify.valid.feed.url": "Debe seleccionar un URL de fuente valido.", + "feeds.validation.interval.not.positive": "El intervalo debe ser un entero positivo.", + "feeds.browse.feeds": "Ver feeds", + "filesystem.empty.directory": "Carpeta vacía", + "filesystem.error.eacces": "Flood no tiene permisos para leer esta carpeta.", + "filesystem.error.enoent": "Esta ruta no existe. Se creara.", + "filesystem.error.unknown": "Se ha producido un error desconocido. Por favor, inténtelo de nuevo.", + "filesystem.fetching": "Cargando estructura de carpeta...", + "filesystem.parent.directory": "Directorio padre", + "filter.all": "Todo", + "filter.status.title": "Filtrar por Estado", + "filter.status.downloading": "Descargando", + "filter.status.seeding": "Sembrando", + "filter.status.completed": "Completo", + "filter.status.active": "Activo", + "filter.status.inactive": "Inactivo", + "filter.status.error": "Error", + "filter.status.stopped": "Detenido", + "filter.status.checking": "Comprobando", + "filter.tracker.title": "Filtrar por Tracker", + "filter.tag.title": "Filtrar por Etiqueta", + "filter.untagged": "Sin Etiqueta", + "general.ago": "hace", + "general.at": "en", + "general.to": "hasta", + "general.of": "de", + "general.clipboard.copy": "Copiar", + "general.clipboard.copied": "Copiado", + "general.error.unknown": "Se ha producido un error desconocido", + "mediainfo.execError": "Se ha encontrado un error al correr Mediainfo. Confirme que Mediainfo este instalado y disponible a Flood en el PATH", + "mediainfo.fetching": "Obteniendo...", + "mediainfo.heading": "Salida de Mediainfo", + "notification.feed.torrent.added.heading": "Suministro de artículo en cola", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "Ninguna notificación para mostrar.", + "notification.torrent.finished.heading": "Descarga Completada", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Error Reportado", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Borrar Todos", + "notification.showing": "Mostrando", + "priority.dont.download": "No Descargar", + "priority.high": "Alta", + "priority.low": "Baja", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Cant. Global de Espacios de Descargas", + "settings.bandwidth.slots.download.label": "Cant. por Torrent de Espacios de Descarga", + "settings.bandwidth.slots.heading": "Disponibilidad de Espacios", + "settings.bandwidth.slots.upload.global.label": "Cant. Global de Espacios de Subida", + "settings.bandwidth.slots.upload.label": "Cant. por Torrent de Espacios de Descarga", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Predeterminados de Desplegable: Descarga", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Predeterminados de Desplegable: Subida", + "settings.bandwidth.transferrate.global.throttle.download": "Regulador Global de Velocidad de Descarga", + "settings.bandwidth.transferrate.global.throttle.upload": "Regulador Global de Velocidad de Subida", + "settings.bandwidth.transferrate.heading": "Reguladores de Velocidad", + "settings.connectivity.dht.label": "Habilitar DHT", + "settings.connectivity.dht.port.label": "Puerto para DHT", + "settings.connectivity.dpd.heading": "Descubrimiento de Peers Descentralizado", + "settings.connectivity.incoming.heading": "Conexiones Entrantes", + "settings.connectivity.ip.hostname.label": "IP/Hostname Presentado", + "settings.connectivity.max.http.connections": "Máxima Cant. de Conexiones HTTP", + "settings.connectivity.peer.exchange.label": "Habilitar Intercambio de Peers", + "settings.connectivity.peers.desired.label": "Cant. de Peers Deseada", + "settings.connectivity.peers.heading": "Pares", + "settings.connectivity.peers.max.label": "Cant. Máxima de Peers", + "settings.connectivity.peers.min.label": "Cant. Mínima de Peers", + "settings.connectivity.peers.seeding.max.label": "Cant. Máxima de Peers en Seeding", + "settings.connectivity.peers.seeding.min.label": "Cant. Mínima de Peers en Seeding", + "settings.connectivity.port.open.label": "Puerto Abierto", + "settings.connectivity.port.randomize.label": "Puerto Aleatorio", + "settings.connectivity.port.range.label": "Rango de Puertos", + "settings.resources.disk.check.hash.label": "Verificar Hash al Terminar", + "settings.resources.disk.download.location.label": "Carpeta de Descargas Predeterminada", + "settings.resources.disk.heading": "Disco", + "settings.resources.max.open.files": "Cant. Máxima de Archivos Abiertos", + "settings.resources.memory.heading": "Memoria", + "settings.resources.memory.max.label": "Utilización Máxima de Memoria", + "settings.tabs.bandwidth": "Ancho de Banda", + "settings.tabs.connectivity": "Conectividad", + "settings.tabs.heading": "Configuración", + "settings.tabs.resources": "Recursos", + "settings.tabs.authentication": "Autenticación", + "settings.tabs.userinterface": "Interfaz de Usuario", + "settings.tabs.diskusage": "Uso del disco", + "settings.tabs.about": "Acerca de", + "settings.ui.locale": "Región", + "settings.ui.language": "Idioma", + "settings.ui.tag.selector.mode": "Preferencia de selector de etiquetas", + "settings.ui.tag.selector.mode.single": "Selección única", + "settings.ui.tag.selector.mode.multi": "Selección múltiple", + "settings.ui.torrent.list": "Exhibición de Torrents", + "settings.ui.torrent.size": "Tamaño de Torrent", + "settings.ui.torrent.size.expanded": "Modo Expandido", + "settings.ui.torrent.size.condensed": "Modo Condensado", + "settings.ui.torrent.details.enabled": "Activado", + "settings.ui.torrent.details.tags.placement": "En el modo expandido, las etiquetas se ven mejor al final de la lista.", + "settings.ui.torrent.context.menu.items.show": "Mostrar", + "settings.ui.displayed.details": "Columnas de detalles de torrent", + "settings.ui.displayed.context.menu.items": "Elementos de menú contextual", + "settings.diskusage.show": "Mostrar", + "settings.diskusage.mount.points": "Puntos de Montura de Uso de Disco", + "settings.about.flood": "Acerca de Flood", + "sidebar.button.feeds": "Fuentes", + "sidebar.button.settings": "Ajustes", + "sidebar.button.speedlimits": "Limites de Velocidad", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Cerrar sesión", + "sidebar.search.placeholder": "Buscar torrents", + "sidebar.transferdata.downloaded": "Descargado", + "sidebar.transferdata.uploaded": "Subido", + "sidebar.speedlimits.download": "DESARROLLO", + "sidebar.speedlimits.upload": "ACTUALIZAR", + "speed.unlimited": "Sín Límite", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "Mb", + "unit.size.gigabyte": "GR", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "h", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Agregar Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Valor opcional cookie-name=cookie-value", + "torrents.add.destination.label": "Destinación", + "torrents.add.destination.placeholder": "Destinación", + "torrents.add.heading": "Agregar Torrents", + "torrents.add.start.label": "Iniciar Descarga", + "torrents.add.tab.file.browse": "o haga clic para navegar", + "torrents.add.tab.file.drop": "Arrastrar archivos hacia aquí,", + "torrents.add.tab.file.title": "Con Archivo", + "torrents.add.tab.url.input.placeholder": "URL de Torrent o Magnet Link", + "torrents.add.tab.url.title": "Con URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Crear", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Etiquetas", + "torrents.create.source.path.label": "Fuente", + "torrents.create.trackers.label": "Rastreadores", + "torrents.create.tracker.input.placeholder": "URL del rastreador", + "torrents.create.base.name.label": "Nombre Base", + "torrents.create.base.name.input.placeholder": "Archivo base o directorio opcional del torrent", + "torrents.create.comment.label": "Comentario", + "torrents.create.comment.input.placeholder": "Comentario opcional en archivo torrent", + "torrents.create.info.source.label": "Info Fuente", + "torrents.create.info.source.input.placeholder": "Entrada opcional de origen en infohash", + "torrents.create.is.private.label": "Privado", + "torrents.create.tags.input.placeholder": "Etiquetas en Flood. No añadido al torrent creado.", + "torrents.destination.base_path": "Usar como Base Path", + "torrents.destination.completed": "Completado", + "torrents.details.actions.pause": "Pausar", + "torrents.details.actions.start": "Comenzar", + "torrents.details.actions.stop": "Parar", + "torrents.details.details": "Detalles", + "torrents.details.files": "Archivos", + "torrents.details.files.loading": "Cargando detalles del archivo...", + "torrents.details.files.download.file": "{count, plural, =1 {Archivo} other {Archivos}}", + "torrents.details.general.comment": "Comentario", + "torrents.details.general.connected": "{connected} conectados de {total}", + "torrents.details.general.date.added": "Agregado en", + "torrents.details.general.date.created": "Creado en", + "torrents.details.general.downloaded": "Descargado", + "torrents.details.general.free.disk.space": "Espacio de Disco Libre", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "General", + "torrents.details.general.heading.torrent": "Torrente", + "torrents.details.general.heading.tracker": "Rastreador", + "torrents.details.general.heading.transfer": "Transferencia", + "torrents.details.general.location": "Carpeta", + "torrents.details.general.none": "Ninguno", + "torrents.details.general.peers": "Pares", + "torrents.details.general.scheduler.ignored": "Ignorado", + "torrents.details.general.scheduler.obeyed": "Seguido", + "torrents.details.general.scheduler": "Programador", + "torrents.details.general.seeds": "Semillas", + "torrents.details.general.size": "Tamaño", + "torrents.details.general.tags": "Etiquetas", + "torrents.details.general.tracker.message": "Mensaje de Tracker", + "torrents.details.general.type.private": "Privado", + "torrents.details.general.type.public": "Publico", + "torrents.details.general.type": "Tipo", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "No existen Peers para este torrent.", + "torrents.details.peers": "Pares", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} archivo seleccionado} other {{countElement} archivos seleccionados}}", + "torrents.details.selected.files.set.priority": "Ajustar Prioridad", + "torrents.details.trackers.no.data": "No existe información de tracker para este torrent.", + "torrents.details.trackers.type": "Tipo", + "torrents.details.trackers": "Rastreadores", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Borrar Filtros", + "torrents.list.context.check.hash": "Verificar Hash", + "torrents.list.context.details": "Detalles de Torrent", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Configurar Carpeta de Descarga", + "torrents.list.context.pause": "Pausar", + "torrents.list.context.download": "Descargar", + "torrents.list.context.priority": "Prioridad", + "torrents.list.context.remove": "Eliminar", + "torrents.list.context.set.tags": "Configurar Etiquetas", + "torrents.list.context.set.trackers": "Definir rastreadores", + "torrents.list.context.start": "Comenzar", + "torrents.list.context.stop": "Parar", + "torrents.list.no.torrents": "No hay torrents", + "torrents.list.drop": "Arrastra los archivos aquí para añadirlos.", + "torrents.list.cannot.connect": "No se puede conectar al cliente.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "de", + "torrents.move.button.set.location": "Configurar", + "torrents.move.button.state.setting": "Guardando...", + "torrents.move.data.label": "Mover archivos", + "torrents.move.check_hash.label": "Comprobar hash", + "torrents.move.heading": "Configurar Carpeta de Descarga", + "torrents.properties.date.added": "Agregado en", + "torrents.properties.comment": "Comentario", + "torrents.properties.creation.date": "Creado en", + "torrents.properties.directory": "Ubicación", + "torrents.properties.download.speed": "Velocidad de Descarga", + "torrents.properties.download.total": "Cant. Descargada", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Espcio de Disco Libre", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignorar Programador", + "torrents.properties.is.private": "Privado", + "torrents.properties.name": "Nombre", + "torrents.properties.percentage": "Porcentaje Descargado", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "Tamaño de Archivo", + "torrents.properties.tags": "Etiquetas", + "torrents.properties.tracker.message": "Mensaje de Tracker", + "torrents.properties.upload.speed": "Velocidad de Subida", + "torrents.properties.upload.total": "Cant. Subida", + "torrents.properties.seeds": "Semillas", + "torrents.properties.peers": "Pares", + "torrents.properties.trackers": "Rastreadores", + "torrents.remove.are.you.sure": "¿Seguro que quiere eliminar {count, plural, =0 {cero torrents} =1 {un torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Eliminar datos", + "torrents.remove.error.no.torrents.selected": "No ha seleccionado un torrent.", + "torrents.remove": "Eliminar Torrents", + "torrents.set.tags.button.set": "Configurar", + "torrents.set.tags.heading": "Configurar Etiquetas", + "torrents.set.tags.enter.tags": "Introducir etiquetas", + "torrents.set.trackers.button.set": "Definir rastreadores", + "torrents.set.trackers.heading": "Definir rastreadores", + "torrents.set.trackers.enter.tracker": "Introduzca un rastreador", + "torrents.set.trackers.loading.trackers": "Cargando rastreadores...", + "torrents.sort.title": "Ordenar Por", + "connection-interruption.heading": "No se puede conectar al cliente", + "status.diskusage.title": "Uso del disco", + "status.diskusage.used": "Usado", + "status.diskusage.free": "Gratis", + "status.diskusage.total": "Total", + "locale.language.auto": "Automátic", + "locale.language.translate": "Empieza a traducir", + "dependency.loading.notifications": "Notificaciones", + "dependency.loading.torrent.taxonomy": "Taxonomía torrente", + "dependency.loading.transfer.rate.details": "Detalles de tasa de transferencia de datos", + "dependency.loading.transfer.history": "Historial de transferencia de datos", + "dependency.loading.torrent.list": "Lista de torrents" +} diff --git a/client/src/javascript/i18n/translations/fi.json b/client/src/javascript/i18n/translations/fi.json new file mode 100644 index 000000000..bc0cc9a56 --- /dev/null +++ b/client/src/javascript/i18n/translations/fi.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Käynnistä Torrentti", + "actionbar.button.stop.torrent": "Lopeta Torrentti", + "actionbar.button.add.torrent": "Lisää Torrentti", + "actionbar.button.remove.torrent": "Poista Torrentti", + "alert.torrent.add": "Onnistuneesti lisättiin {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "{countElement} ei voitu lisätä {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Onnistuneesti siirretty {countElement} {count, plural, =1 {torrent} other {torrentit}}.", + "alert.torrent.move.failed": "{countElement} ei voitu siirtää {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Onnistuneesti poistettiin {countElement} {count, plural, =1 {torrent} other {torrentit}}.", + "alert.torrent.remove.failed": "{countElement} poisto epäonnistui, {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Asetukset tallennettu onnistuneesti.", + "auth.add.user": "Lisää Käyttäjä", + "auth.create.account": "Luo Tili", + "auth.create.an.account": "Luo tili", + "auth.create.an.account.intro": "Tervetuloa tulvaan!", + "auth.current.user": "Nykyinen Käyttäjä", + "auth.error.username.empty": "Käyttäjätunnus ei voi olla tyhjä.", + "auth.error.password.empty": "Salasana ei voi olla tyhjä.", + "auth.input.clear": "Clear", + "auth.log.in": "Kirjaudu Sisään", + "auth.login": "Kirjaudu", + "auth.login.intro": "Kirjaudu sisään tilillesi.", + "auth.password": "Salasana", + "auth.user.accounts": "Käyttäjän Tilit", + "auth.username": "Käyttäjätunnus", + "auth.admin": "Ylläpitäjä", + "auth.message.not.admin": "Käyttäjä ei ole ylläpitäjä", + "button.add": "Lisää", + "button.cancel": "Peruuta", + "button.close": "Close", + "button.download": "Lataa", + "button.no": "Ei", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Tallenna Asetukset", + "button.save.feed": "Tallenna", + "button.state.adding": "Lisätään...", + "button.yes": "Kyllä", + "button.new": "Uusi", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Yhteyttä ei voitu todentaa.", + "connection.settings.client.select": "Asiakas", + "connection.settings.error.empty": "Yhteyden asetukset eivät voi olla tyhjä.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Yhteyden Tyyppi", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Isäntä", + "connection.settings.rtorrent.host.input.placeholder": "Palvelimen nimi tai IP", + "connection.settings.rtorrent.port": "Portti", + "connection.settings.rtorrent.port.input.placeholder": "Portti", + "connection.settings.rtorrent.socket": "Polku", + "connection.settings.rtorrent.socket.input.placeholder": "Polku pistorasiaan", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL-osoite", + "connection.settings.qbittorrent.url.input.placeholder": "URL qBittorrent Web API", + "connection.settings.qbittorrent.username": "Käyttäjätunnus", + "connection.settings.qbittorrent.username.input.placeholder": "Käyttäjätunnus", + "connection.settings.qbittorrent.password": "Salasana", + "connection.settings.qbittorrent.password.input.placeholder": "Salasana", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Yhteyden Ongelma", + "connectivity.modal.content": "Ei voi yhdistää asiakasta. Ole hyvä ja päivitä yhteysasetuksia.", + "feeds.add.automatic.download.rule": "Lisää Lataussääntö", + "feeds.add.feed": "Lisää Syöte", + "feeds.applicable.feed": "Sovellettava Syöte", + "feeds.apply.tags": "Käytä Tunnisteita", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Ohita Kuvio", + "feeds.existing.feeds": "Olemassa Olevat Syötteet", + "feeds.existing.rules": "Voimassa Olevat Säännöt", + "feeds.interval": "Aikaväli", + "feeds.label": "Tunniste", + "feeds.match.count": "{count, plural, =1 {# ottelua} other {# osumaa}}", + "feeds.match.pattern": "Täsmää Kuvio", + "feeds.match": "Osuma", + "feeds.exclude": "Ohita", + "feeds.no.feeds.available": "Syötteitä ei ole saatavilla.", + "feeds.no.feeds.defined": "Syötteitä ei ole määritelty.", + "feeds.no.items.matching": "Ei kohteita, jotka vastaavat hakutermiä.", + "feeds.no.rules.defined": "Sääntöjä ei ole määritelty.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Valitse Syöte", + "feeds.select.interval": "Aikaväli", + "feeds.start.on.load": "Aloita latauksessa", + "feeds.tabs.download.rules": "Lataa Säännöt", + "feeds.tabs.feeds": "Syötteet", + "feeds.tabs.heading": "Torrent- Syötteet", + "feeds.tags": "Tunnisteet", + "feeds.test.match": "Testaa Vastaavuus Kuvio", + "feeds.time.hr": "Tuntia", + "feeds.time.min": "Minuuttia", + "feeds.time.day": "Päivää", + "feeds.torrent.destination": "Torrent- Kohde", + "feeds.url": "URL-osoite", + "feeds.search": "Hae termi", + "feeds.search.term": "Hae termi", + "feeds.validation.invalid.regular.expression": "Virheellinen säännöllinen lauseke.", + "feeds.validation.must.select.feed": "Sinun täytyy valita syöte.", + "feeds.validation.must.specify.destination": "Sinun on määritettävä määränpää.", + "feeds.validation.must.specify.label": "Sinun on määritettävä nimi.", + "feeds.validation.must.specify.valid.feed.url": "Sinun on määritettävä kelvollinen syötteen URL-osoite.", + "feeds.validation.interval.not.positive": "Aikavälin on oltava positiivinen kokonaisluku.", + "feeds.browse.feeds": "Selaa syötteitä", + "filesystem.empty.directory": "Tyhjä hakemisto.", + "filesystem.error.eacces": "Tulvalla ei ole oikeuksia lukea tätä hakemistoa.", + "filesystem.error.enoent": "Tätä polkua ei ole olemassa. Se luodaan.", + "filesystem.error.unknown": "Tapahtui tuntematon virhe. Yritä uudelleen.", + "filesystem.fetching": "Haetaan kansioraketta...", + "filesystem.parent.directory": "Vanhemman Hakemisto", + "filter.all": "Kaikki", + "filter.status.title": "Suodata tilan mukaan", + "filter.status.downloading": "Ladataan", + "filter.status.seeding": "Lähetetään", + "filter.status.completed": "Complete", + "filter.status.active": "Aktiivinen", + "filter.status.inactive": "Passiivinen", + "filter.status.error": "Virhe", + "filter.status.stopped": "Pysäytetty", + "filter.status.checking": "Tarkistetaan", + "filter.tracker.title": "Suodata seurantapalvelimen mukaan", + "filter.tag.title": "Suodata tunnisteen mukaan", + "filter.untagged": "Merkitsemätön", + "general.ago": "sitten", + "general.at": "klo", + "general.to": "päättyen", + "general.of": "jostakin", + "general.clipboard.copy": "Kopioi", + "general.clipboard.copied": "Kopioitu", + "general.error.unknown": "Tapahtui tuntematon virhe", + "mediainfo.execError": "Palvelimella tapahtui virhe. Tarkista, että mediainfo on asennettu ja saatavilla PATH to Flood.", + "mediainfo.fetching": "Haetaan...", + "mediainfo.heading": "Mediainfon Ulostulo", + "notification.feed.torrent.added.heading": "Syötteen Kohdetta Jonossa", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Lataaminen Valmis", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Virhe Raportoitu", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Tyhjennä Kaikki", + "notification.showing": "Näytetään", + "priority.dont.download": "Älä Lataa", + "priority.high": "Korkea", + "priority.low": "Matala", + "priority.normal": "Normaali", + "settings.bandwidth.slots.download.global.label": "Lataa Slots Global", + "settings.bandwidth.slots.download.label": "Lataa Paikkoja Torrenttia Kohti", + "settings.bandwidth.slots.heading": "Paikan Saatavuus", + "settings.bandwidth.slots.upload.global.label": "Lataa Slots Globaali", + "settings.bandwidth.slots.upload.label": "Lataa Paikkoja Torrenttia Kohti", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Pudotusvalikon Esiasetukset: Lataus", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Pudotusvalikon Esiasetukset: Lataus", + "settings.bandwidth.transferrate.global.throttle.download": "Maailmanlaajuinen Latausnopeus Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Yleinen Lähetysnopeus Throttle", + "settings.bandwidth.transferrate.heading": "Siirto^hinta Throttles", + "settings.connectivity.dht.label": "Ota Dht Käyttöön", + "settings.connectivity.dht.port.label": "Dht Portti", + "settings.connectivity.dpd.heading": "Hajautettu Peer Discovery", + "settings.connectivity.incoming.heading": "Tulevat Yhteydet", + "settings.connectivity.ip.hostname.label": "Raportoitu IP/isäntänimi", + "settings.connectivity.max.http.connections": "Maksimi Http Yhteydet", + "settings.connectivity.peer.exchange.label": "Ota Käyttöön Vertaisvaihto", + "settings.connectivity.peers.desired.label": "Käyttäjät Haluttu", + "settings.connectivity.peers.heading": "Käyttäjät", + "settings.connectivity.peers.max.label": "Käyttäjien Enimmäismäärä", + "settings.connectivity.peers.min.label": "Käyttäjien Minimi", + "settings.connectivity.peers.seeding.max.label": "Käyttäjien Maksimi Lähetys", + "settings.connectivity.peers.seeding.min.label": "Käyttäjien Vähimmäismäärä", + "settings.connectivity.port.open.label": "Avaa Portti", + "settings.connectivity.port.randomize.label": "Satunnaista Portti", + "settings.connectivity.port.range.label": "Portti Alue", + "settings.resources.disk.check.hash.label": "Vahvista täydennyksen vihje", + "settings.resources.disk.download.location.label": "Oletus Lataus Hakemisto", + "settings.resources.disk.heading": "Levy", + "settings.resources.max.open.files": "Enimmäismäärä Avoimia Tiedostoja", + "settings.resources.memory.heading": "Muisti", + "settings.resources.memory.max.label": "Maksimi Muistin Käyttö", + "settings.tabs.bandwidth": "Kaistanleveys", + "settings.tabs.connectivity": "Yhteys", + "settings.tabs.heading": "Asetukset", + "settings.tabs.resources": "Resurssit", + "settings.tabs.authentication": "Todennus", + "settings.tabs.userinterface": "Käyttöliittymä", + "settings.tabs.diskusage": "Levyn Käyttö", + "settings.tabs.about": "Tietoja", + "settings.ui.locale": "Lokaatio", + "settings.ui.language": "Kieli", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent- Listan Näyttö", + "settings.ui.torrent.size": "Torrenttien Koko", + "settings.ui.torrent.size.expanded": "Laajennettu Näkymä", + "settings.ui.torrent.size.condensed": "Tiivistetty Näkymä", + "settings.ui.torrent.details.enabled": "Käytössä", + "settings.ui.torrent.details.tags.placement": "Laajennetussa näkymässä tunnisteet toimivat parhaiten listan lopussa.", + "settings.ui.torrent.context.menu.items.show": "Näytä", + "settings.ui.displayed.details": "Torrent- Yksityiskohtaiset Sarakkeet", + "settings.ui.displayed.context.menu.items": "Kontekstivalikon Nimikkeet", + "settings.diskusage.show": "Näytä", + "settings.diskusage.mount.points": "Levynkäytön Pisteet", + "settings.about.flood": "Tietoja Tulvasta", + "sidebar.button.feeds": "Syötteet", + "sidebar.button.settings": "Asetukset", + "sidebar.button.speedlimits": "Nopeusrajat", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Kirjaudu Ulos", + "sidebar.search.placeholder": "Etsi torrent-tiedostoja", + "sidebar.transferdata.downloaded": "Ladattu", + "sidebar.transferdata.uploaded": "Ladattu", + "sidebar.speedlimits.download": "LATAA", + "sidebar.speedlimits.upload": "LATAA", + "speed.unlimited": "Rajoittamaton", + "unit.size.byte": "B", + "unit.size.kilobyte": "kt", + "unit.size.megabyte": "Mt", + "unit.size.gigabyte": "Gt", + "unit.size.terabyte": "Tt", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "vk", + "unit.time.day": "pv", + "unit.time.hour": "h", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Lisää Torrentti", + "torrents.add.cookies.label": "Evästeet", + "torrents.add.cookies.input.placeholder": "Valinnainen evästeen nimi = evästeen arvo", + "torrents.add.destination.label": "Kohde", + "torrents.add.destination.placeholder": "Kohde", + "torrents.add.heading": "Lisää Torrentit", + "torrents.add.start.label": "Käynnistä Torrentti", + "torrents.add.tab.file.browse": "tai klikkaa selataksesi", + "torrents.add.tab.file.drop": "Pudota joitakin tiedostoja tähän.", + "torrents.add.tab.file.title": "Tiedoston Mukaan", + "torrents.add.tab.url.input.placeholder": "Torrent- URL tai Magnet- linkki", + "torrents.add.tab.url.title": "URL:n Mukaan", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Luo", + "torrents.add.torrents.label": "Torrentit", + "torrents.add.tags": "Tunnisteet", + "torrents.create.source.path.label": "Lähde", + "torrents.create.trackers.label": "Seurantapalvelimet", + "torrents.create.tracker.input.placeholder": "Seurantapalvelimen URL", + "torrents.create.base.name.label": "Perusnimi", + "torrents.create.base.name.input.placeholder": "Valinnainen perustiedosto tai torrent-tiedoston nimi", + "torrents.create.comment.label": "Kommentti", + "torrents.create.comment.input.placeholder": "Valinnainen kommentti torrent-tiedostossa", + "torrents.create.info.source.label": "Tietojen Lähde", + "torrents.create.info.source.input.placeholder": "Valinnainen lähdemerkintä infohash", + "torrents.create.is.private.label": "Yksityinen", + "torrents.create.tags.input.placeholder": "Tunnisteet Tulossa. Ei lisätty luotuun torrenttiin.", + "torrents.destination.base_path": "Käytä peruspolkuna", + "torrents.destination.completed": "Valmis", + "torrents.details.actions.pause": "Tauko", + "torrents.details.actions.start": "Aloita", + "torrents.details.actions.stop": "Pysäytä", + "torrents.details.details": "Yksityiskohdat", + "torrents.details.files": "Tiedostot", + "torrents.details.files.loading": "Ladataan tiedoston tietoa...", + "torrents.details.files.download.file": "{count, plural, =1 {Lataa tiedosto} other {Lataa Tiedostoja}}", + "torrents.details.general.comment": "Kommentti", + "torrents.details.general.connected": "{connected} yhdistetty {total}", + "torrents.details.general.date.added": "Lisätty", + "torrents.details.general.date.created": "Luonti Päivämäärä", + "torrents.details.general.downloaded": "Ladattu", + "torrents.details.general.free.disk.space": "Vapaa Levytila", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Yleiset", + "torrents.details.general.heading.torrent": "Torrentti", + "torrents.details.general.heading.tracker": "Seurantapalvelin", + "torrents.details.general.heading.transfer": "Siirto", + "torrents.details.general.location": "Sijainti", + "torrents.details.general.none": "Ei Mitään", + "torrents.details.general.peers": "Käyttäjät", + "torrents.details.general.scheduler.ignored": "Ohitettu", + "torrents.details.general.scheduler.obeyed": "Ylimääräinen", + "torrents.details.general.scheduler": "Ajastus", + "torrents.details.general.seeds": "Siemenet", + "torrents.details.general.size": "Koko", + "torrents.details.general.tags": "Tunnisteet", + "torrents.details.general.tracker.message": "Seurantapalvelimen Viesti", + "torrents.details.general.type.private": "Yksityinen", + "torrents.details.general.type.public": "Julkinen", + "torrents.details.general.type": "Tyyppi", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Tälle torrentille ei ole vertaistietoja.", + "torrents.details.peers": "Käyttäjät", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} valittu tiedosto} other {{countElement} valitut tiedostot}}", + "torrents.details.selected.files.set.priority": "Aseta Prioriteetti", + "torrents.details.trackers.no.data": "Tälle torrentille ei ole seurantatietoja.", + "torrents.details.trackers.type": "Tyyppi", + "torrents.details.trackers": "Seurantapalvelimet", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Tyhjennä Suodattimet", + "torrents.list.context.check.hash": "Tarkista Hash", + "torrents.list.context.details": "Torrent-tiedoston Tiedot", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Aseta Torrent- Sijainti", + "torrents.list.context.pause": "Tauko", + "torrents.list.context.download": "Lataa", + "torrents.list.context.priority": "Prioriteetti", + "torrents.list.context.remove": "Poista", + "torrents.list.context.set.tags": "Aseta Tagit", + "torrents.list.context.set.trackers": "Aseta Seurantapalvelimet", + "torrents.list.context.start": "Aloita", + "torrents.list.context.stop": "Pysäytä", + "torrents.list.no.torrents": "Ei näytettäviä torrent-tiedostoja.", + "torrents.list.drop": "Pudota tiedostot tähän lisätäksesi ne.", + "torrents.list.cannot.connect": "Ei voi muodostaa yhteyttä asiakkaaseen.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "jostakin", + "torrents.move.button.set.location": "Aseta Sijainti", + "torrents.move.button.state.setting": "Asetetaan...", + "torrents.move.data.label": "Siirrä tiedot", + "torrents.move.check_hash.label": "Tarkista tiivistys", + "torrents.move.heading": "Aseta Torrent- Sijainti", + "torrents.properties.date.added": "Lisätty", + "torrents.properties.comment": "Kommentti", + "torrents.properties.creation.date": "Luonti Päivämäärä", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Latauksen Nopeus", + "torrents.properties.download.total": "Ladattu", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Vapaa Levytila", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ohita Ajastin", + "torrents.properties.is.private": "Yksityinen", + "torrents.properties.name": "Nimi", + "torrents.properties.percentage": "Prosentti Valmis", + "torrents.properties.ratio": "Suhde", + "torrents.properties.size": "Tiedoston Koko", + "torrents.properties.tags": "Tunnisteet", + "torrents.properties.tracker.message": "Seurantapalvelimen Viesti", + "torrents.properties.upload.speed": "Lähetyksen Nopeus", + "torrents.properties.upload.total": "Ladattu", + "torrents.properties.seeds": "Siemenet", + "torrents.properties.peers": "Käyttäjät", + "torrents.properties.trackers": "Seurantapalvelimet", + "torrents.remove.are.you.sure": "Oletko varma, että haluat poistaa {count, plural, =1 {# torrent} other {# torrent}}?", + "torrents.remove.delete.data": "Poista tiedot", + "torrents.remove.error.no.torrents.selected": "Et ole valinnut yhtään torrenttia.", + "torrents.remove": "Poista Torrentit", + "torrents.set.tags.button.set": "Aseta Tagit", + "torrents.set.tags.heading": "Aseta Tagit", + "torrents.set.tags.enter.tags": "Syötä tunnisteet", + "torrents.set.trackers.button.set": "Aseta Seurantapalvelimet", + "torrents.set.trackers.heading": "Aseta Seurantapalvelimet", + "torrents.set.trackers.enter.tracker": "Anna seurantapalvelin", + "torrents.set.trackers.loading.trackers": "Ladataan seurantaohjelmia...", + "torrents.sort.title": "Järjestä Mukaan", + "connection-interruption.heading": "Asiakasta ei voi yhdistää", + "status.diskusage.title": "Levyn Käyttö", + "status.diskusage.used": "Käytetty", + "status.diskusage.free": "Ilmainen", + "status.diskusage.total": "Yhteensä", + "locale.language.auto": "Automaattinen", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Ilmoitukset", + "dependency.loading.torrent.taxonomy": "Torrent-Taksonomia", + "dependency.loading.transfer.rate.details": "Tietojen Siirron Hintatiedot", + "dependency.loading.transfer.history": "Tietojen Siirron Historia", + "dependency.loading.torrent.list": "Torrent- Luettelo" +} diff --git a/client/src/javascript/i18n/translations/fr.json b/client/src/javascript/i18n/translations/fr.json new file mode 100644 index 000000000..f192e6fcf --- /dev/null +++ b/client/src/javascript/i18n/translations/fr.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Démarrer le torrent", + "actionbar.button.stop.torrent": "Arrêter le torrent", + "actionbar.button.add.torrent": "Ajouter un torrent", + "actionbar.button.remove.torrent": "Supprimer le torrent", + "alert.torrent.add": "L'ajout de {countElement} {count, plural, =1 {torrent} other {torrents}} a réussi.", + "alert.torrent.add.failed": "L'ajout de {countElement} {count, plural, =1 {torrent} other {torrents}} a échoué.", + "alert.torrent.move": "Le déplacement de {countElement} {count, plural, =1 {torrent} other {torrents}} a réussi.", + "alert.torrent.move.failed": "Le déplacement de {countElement} {count, plural, =1 {torrent} other {torrents}} a échoué.", + "alert.torrent.remove": "La suppression de {countElement} {count, plural, =1 {torrent} other {torrents}} a réussi.", + "alert.torrent.remove.failed": "La suppression de {countElement} {count, plural, =1 {torrent} other {torrents}} a échoué.", + "alert.settings.saved": "Paramètres modifiés.", + "auth.add.user": "Ajouter un utilisateur", + "auth.create.account": "Créer un compte", + "auth.create.an.account": "Créer un compte", + "auth.create.an.account.intro": "Bienvenue dans Flood !", + "auth.current.user": "Utilisateur actuel", + "auth.error.username.empty": "Le nom d'utilisateur ne peut pas être vide.", + "auth.error.password.empty": "Le mot de passe ne peut pas être vide.", + "auth.input.clear": "Effacer", + "auth.log.in": "Connexion", + "auth.login": "Identifiant", + "auth.login.intro": "Se connecter.", + "auth.password": "Mot de passe", + "auth.user.accounts": "Comptes", + "auth.username": "Nom d'utilisateur", + "auth.admin": "Administrateur", + "auth.message.not.admin": "L'utilisateur n'est pas administrateur", + "button.add": "Ajouter", + "button.cancel": "Annuler", + "button.close": "Fermer", + "button.download": "Télécharger", + "button.no": "Non", + "button.ok": "OK", + "button.retry": "Réessayer", + "button.save": "Enregistrer les paramètres", + "button.save.feed": "Enregistrer", + "button.state.adding": "Ajout...", + "button.yes": "Oui", + "button.new": "Nouveau", + "connection-interruption.action.selection.retry": "Réessayer avec les paramètres de connexion actuels du client", + "connection-interruption.action.selection.config": "Mettre à jour les paramètres de connexion client", + "connection-interruption.not.admin": "Veuillez contacter votre administrateur Flood si cela persiste.", + "connection-interruption.verification-error": "La connexion n'a pas pu être établie.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Les paramètres de connexion ne peuvent pas être vides.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Type de connexion", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposer rTorrent via TCP peut permettre une élévation des privilèges.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Hôte", + "connection.settings.rtorrent.host.input.placeholder": "Nom d'hôte ou IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Chemin d'accès", + "connection.settings.rtorrent.socket.input.placeholder": "Chemin d'accès au socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL de l'API Web qBittorrent", + "connection.settings.qbittorrent.username": "Nom d'utilisateur", + "connection.settings.qbittorrent.username.input.placeholder": "Nom d'utilisateur", + "connection.settings.qbittorrent.password": "Mot de passe", + "connection.settings.qbittorrent.password.input.placeholder": "Mot de passe", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL de l'interface RPC de Transmission", + "connection.settings.transmission.username": "Nom d'utilisateur", + "connection.settings.transmission.username.input.placeholder": "Nom d'utilisateur", + "connection.settings.transmission.password": "Mot de passe", + "connection.settings.transmission.password.input.placeholder": "Mot de passe", + "connectivity.modal.title": "Problème de connectivité", + "connectivity.modal.content": "Impossible de se connecter au client. Veuillez mettre à jour les paramètres de connexion.", + "feeds.add.automatic.download.rule": "Ajouter une règle de téléchargement", + "feeds.add.feed": "Ajouter un flux", + "feeds.applicable.feed": "Applicable au flux", + "feeds.apply.tags": "Appliquer les tags", + "feeds.check": "Valider la règle en l'essayant. Non enregistré ou envoyé.", + "feeds.exclude.pattern": "Motif pour exclusion", + "feeds.existing.feeds": "Flux existants", + "feeds.existing.rules": "Règles existantes", + "feeds.interval": "Intervalle", + "feeds.label": "Étiquette", + "feeds.match.count": "{count, plural, =0 {# correspond} =1 {# correspond} other {# correspondent}}", + "feeds.match.pattern": "Motif pour inclusion", + "feeds.match": "Correspondance", + "feeds.exclude": "Exclure", + "feeds.no.feeds.available": "Aucun flux disponible.", + "feeds.no.feeds.defined": "Aucun flux défini.", + "feeds.no.items.matching": "Aucun élément ne correspond à la recherche.", + "feeds.no.rules.defined": "Aucune règle définie.", + "feeds.regEx": "Expression régulière", + "feeds.select.feed": "Sélectionner un flux", + "feeds.select.interval": "Intervalle", + "feeds.start.on.load": "Démarrer immédiatement", + "feeds.tabs.download.rules": "Règles de téléchargement", + "feeds.tabs.feeds": "Flux", + "feeds.tabs.heading": "Flux de torrents", + "feeds.tags": "Tags", + "feeds.test.match": "Tester le motif", + "feeds.time.hr": "Heures", + "feeds.time.min": "Minutes", + "feeds.time.day": "Jours", + "feeds.torrent.destination": "Emplacement du torrent", + "feeds.url": "URL", + "feeds.search": "Recherche", + "feeds.search.term": "Recherche", + "feeds.validation.invalid.regular.expression": "Expression régulière invalide.", + "feeds.validation.must.select.feed": "Vous devez choisir un flux.", + "feeds.validation.must.specify.destination": "Vous devez définir un emplacement.", + "feeds.validation.must.specify.label": "Vous devez définir un label.", + "feeds.validation.must.specify.valid.feed.url": "Vous devez définir une URL de flux valide.", + "feeds.validation.interval.not.positive": "L'intervalle doit être un entier positif.", + "feeds.browse.feeds": "Parcourir les flux", + "filesystem.empty.directory": "Répertoire vide.", + "filesystem.error.eacces": "Flood n'a pas la permission de lire ce répertoire.", + "filesystem.error.enoent": "Cet emplacement n'existe pas. Il sera créé.", + "filesystem.error.unknown": "Une erreur inconnue s'est produite. Veuillez réessayer.", + "filesystem.fetching": "Récupération de la structure du répertoire...", + "filesystem.parent.directory": "Dossier parent", + "filter.all": "Tout", + "filter.status.title": "Filtrer par Status", + "filter.status.downloading": "En téléchargement", + "filter.status.seeding": "En source", + "filter.status.completed": "Terminé", + "filter.status.active": "Actif", + "filter.status.inactive": "Inactif", + "filter.status.error": "Erreur", + "filter.status.stopped": "Arrêté", + "filter.status.checking": "En cours de vérification", + "filter.tracker.title": "Filtrer par tracker", + "filter.tag.title": "Filtrer par étiquette", + "filter.untagged": "Sans étiquette", + "general.ago": "il y a", + "general.at": "à", + "general.to": "à", + "general.of": "de", + "general.clipboard.copy": "Copier", + "general.clipboard.copied": "Copié", + "general.error.unknown": "Une erreur inconnue s'est produite", + "mediainfo.execError": "Une erreur est survenue lors de l'exécution de mediainfo sur le serveur. Vérifiez que mediainfo est installé et disponible dans le PATH de Flood.", + "mediainfo.fetching": "Récupération en cours...", + "mediainfo.heading": "Résultat mediainfo", + "notification.feed.torrent.added.heading": "Élément de flux en file d'attente", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "Aucune notification à afficher.", + "notification.torrent.finished.heading": "Téléchargement terminé", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Erreur signalée", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Tout effacer", + "notification.showing": "Affichage de", + "priority.dont.download": "Ne pas télécharger", + "priority.high": "Élevée", + "priority.low": "Basse", + "priority.normal": "Normale", + "settings.bandwidth.slots.download.global.label": "Slots de téléchargement globaux", + "settings.bandwidth.slots.download.label": "Slots de téléchargement par torrent", + "settings.bandwidth.slots.heading": "Configuration des slots", + "settings.bandwidth.slots.upload.global.label": "Slots d'envoi global", + "settings.bandwidth.slots.upload.label": "Slots d'essaimage par torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Pré-réglages des vitesses : téléchargement", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Pré-réglages des vitesses : Envoi", + "settings.bandwidth.transferrate.global.throttle.download": "Limite globale de téléchargement", + "settings.bandwidth.transferrate.global.throttle.upload": "Limite globale d'envoi", + "settings.bandwidth.transferrate.heading": "Limites de vitesse de transfert", + "settings.connectivity.dht.label": "Activer le DHT", + "settings.connectivity.dht.port.label": "Port DHT", + "settings.connectivity.dpd.heading": "Découverte décentralisée de pairs", + "settings.connectivity.incoming.heading": "Connexions entrantes", + "settings.connectivity.ip.hostname.label": "Adresse IP / nom d'hôte affichée", + "settings.connectivity.max.http.connections": "Limite de connexions HTTP", + "settings.connectivity.peer.exchange.label": "Activer l'échange de pairs", + "settings.connectivity.peers.desired.label": "Pairs désirés", + "settings.connectivity.peers.heading": "Pairs", + "settings.connectivity.peers.max.label": "Limite de pairs en téléchargement", + "settings.connectivity.peers.min.label": "Seuil minimum de pairs", + "settings.connectivity.peers.seeding.max.label": "Seuil maximum de pairs en source", + "settings.connectivity.peers.seeding.min.label": "Seuil minimum de pairs en essaimage", + "settings.connectivity.port.open.label": "Ouvrir le port", + "settings.connectivity.port.randomize.label": "Port aléatoire", + "settings.connectivity.port.range.label": "Plage de ports", + "settings.resources.disk.check.hash.label": "Vérifier le hash après le téléchargement", + "settings.resources.disk.download.location.label": "Répertoire de téléchargement par défaut", + "settings.resources.disk.heading": "Disque", + "settings.resources.max.open.files": "Limite de fichiers ouverts", + "settings.resources.memory.heading": "Mémoire", + "settings.resources.memory.max.label": "Limite d'utilisation de mémoire", + "settings.tabs.bandwidth": "Bande passante", + "settings.tabs.connectivity": "Connectivité", + "settings.tabs.heading": "Paramètres", + "settings.tabs.resources": "Ressources", + "settings.tabs.authentication": "Authentification", + "settings.tabs.userinterface": "Interface utilisateur", + "settings.tabs.diskusage": "Utilisation du disque", + "settings.tabs.about": "À propos", + "settings.ui.locale": "Paramètres régionaux", + "settings.ui.language": "Langue", + "settings.ui.tag.selector.mode": "Sélecteur de Préférence", + "settings.ui.tag.selector.mode.single": "Sélection Unique", + "settings.ui.tag.selector.mode.multi": "Sélection Multiple", + "settings.ui.torrent.list": "Affichage de la liste des torrents", + "settings.ui.torrent.size": "Taille du torrent", + "settings.ui.torrent.size.expanded": "Vue étendue", + "settings.ui.torrent.size.condensed": "Vue condensée", + "settings.ui.torrent.details.enabled": "Activé", + "settings.ui.torrent.details.tags.placement": "Dans la vue étendue, il vaut mieux placer les tags dernier.", + "settings.ui.torrent.context.menu.items.show": "Afficher", + "settings.ui.displayed.details": "Colonnes de détails du torrent", + "settings.ui.displayed.context.menu.items": "Éléments du menu contextuel", + "settings.diskusage.show": "Afficher", + "settings.diskusage.mount.points": "Points de montage à observer", + "settings.about.flood": "À propos de Flood", + "sidebar.button.feeds": "Flux", + "sidebar.button.settings": "Paramètres", + "sidebar.button.speedlimits": "Limites de Vitesse", + "sidebar.button.theme.dark": "Thème Sombre", + "sidebar.button.theme.light": "Thème Clair", + "sidebar.button.log.out": "Déconnexion", + "sidebar.search.placeholder": "Rechercher les torrents", + "sidebar.transferdata.downloaded": "Téléchargé", + "sidebar.transferdata.uploaded": "Envoyé", + "sidebar.speedlimits.download": "TÉLÉCHARGEMENT", + "sidebar.speedlimits.upload": "ENVOI", + "speed.unlimited": "Illimité", + "unit.size.byte": "o", + "unit.size.kilobyte": "ko", + "unit.size.megabyte": "Mo", + "unit.size.gigabyte": "Go", + "unit.size.terabyte": "To", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "an", + "unit.time.week": "sem", + "unit.time.day": "j", + "unit.time.hour": "h", + "unit.time.minute": "min", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Ajouter un torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "cookie-name=cookie-value optionnel", + "torrents.add.destination.label": "Emplacement", + "torrents.add.destination.placeholder": "Emplacement", + "torrents.add.heading": "Ajouter des torrents", + "torrents.add.start.label": "Démarrer", + "torrents.add.tab.file.browse": "ou cliquez pour sélectionner", + "torrents.add.tab.file.drop": "Glissez-déposez des fichiers ici,", + "torrents.add.tab.file.title": "Par fichier", + "torrents.add.tab.url.input.placeholder": "URL du torrent ou lien magnet", + "torrents.add.tab.url.title": "Par URL", + "torrents.add.tab.url.register.magnet.handler": "S'enregistrer pour gérer les liens magnet", + "torrents.add.tab.create.title": "Créer", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "URL du tracker", + "torrents.create.base.name.label": "Nom de base", + "torrents.create.base.name.input.placeholder": "Fichier de base optionnel ou nom de répertoire du torrent", + "torrents.create.comment.label": "Commenter", + "torrents.create.comment.input.placeholder": "Commentaire optionnel dans le fichier torrent", + "torrents.create.info.source.label": "Source d'info", + "torrents.create.info.source.input.placeholder": "Saisie optionnelle de la source dans le tableau de bord", + "torrents.create.is.private.label": "Privé", + "torrents.create.tags.input.placeholder": "Tags dans le Déluge. Non ajouté au torrent créé.", + "torrents.destination.base_path": "Utiliser comme chemin de base", + "torrents.destination.completed": "Terminé", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Démarrer", + "torrents.details.actions.stop": "Arrêter", + "torrents.details.details": "Détails", + "torrents.details.files": "Fichiers", + "torrents.details.files.loading": "Chargement des détails du fichier...", + "torrents.details.files.download.file": "{count, plural, =1 {Téléchargez le fichier} other {Téléchargez les fichiers}}", + "torrents.details.general.comment": "Commentaire", + "torrents.details.general.connected": "{connected} connectés sur {total}", + "torrents.details.general.date.added": "Ajouté", + "torrents.details.general.date.created": "Créé", + "torrents.details.general.downloaded": "Téléchargé", + "torrents.details.general.free.disk.space": "Espace disque disponible", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Général", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfert", + "torrents.details.general.location": "Emplacement", + "torrents.details.general.none": "Aucun", + "torrents.details.general.peers": "Pairs", + "torrents.details.general.scheduler.ignored": "Ignoré", + "torrents.details.general.scheduler.obeyed": "Conforme", + "torrents.details.general.scheduler": "Planificateur", + "torrents.details.general.seeds": "Sources", + "torrents.details.general.size": "Taille", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Message du tracker", + "torrents.details.general.type.private": "Privé", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Type de texte", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Il n'y a aucun pair pour ce torrent.", + "torrents.details.peers": "Pairs", + "torrents.details.selected.files": "{count, plural, =0 {aucun fichier sélectionné} =1 {un fichier sélectionné} other {{countElement} fichiers sélectionnés}}", + "torrents.details.selected.files.set.priority": "Définir la priorité", + "torrents.details.trackers.no.data": "Il n'y a aucun tracker actif pour ce torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Générer un lien Magnet", + "torrents.generate.magnet.loading.trackers": "Chargement des trackers...", + "torrents.generate.magnet.private.torrent": "C'est un torrent privé.", + "torrents.generate.magnet.magnet": "Lien Magnet", + "torrents.generate.magnet.magnet.with.trackers": "Lien Magnet avec Trackers", + "torrents.list.clear.filters": "Effacer les filtres", + "torrents.list.context.check.hash": "Vérifier le hash", + "torrents.list.context.details": "Détails du torrent", + "torrents.list.context.generate.magnet": "Générer un lien Magnet", + "torrents.list.context.move": "Définir l'emplacement du torrent", + "torrents.list.context.pause": "Mettre en pause", + "torrents.list.context.download": "Télécharger", + "torrents.list.context.priority": "Priorité", + "torrents.list.context.remove": "Supprimer", + "torrents.list.context.set.tags": "Définir les tags", + "torrents.list.context.set.trackers": "Définir les mouchards", + "torrents.list.context.start": "Démarrer", + "torrents.list.context.stop": "Arrêter", + "torrents.list.no.torrents": "Aucun torrent à afficher.", + "torrents.list.drop": "Déposez les fichiers ici pour les ajouter.", + "torrents.list.cannot.connect": "Impossible de se connecter au client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "sur", + "torrents.move.button.set.location": "Définir l'emplacement", + "torrents.move.button.state.setting": "En cours...", + "torrents.move.data.label": "Déplacer les données", + "torrents.move.check_hash.label": "Vérifier le hash", + "torrents.move.heading": "Définir l'emplacement du torrent", + "torrents.properties.date.added": "Ajouté", + "torrents.properties.comment": "Commentaire", + "torrents.properties.creation.date": "Date de création", + "torrents.properties.directory": "Emplacement", + "torrents.properties.download.speed": "Vitesse de téléchargement", + "torrents.properties.download.total": "Téléchargé", + "torrents.properties.eta": "Date de fin estimée", + "torrents.properties.free.disk.space": "Espace libre", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignorer la planification", + "torrents.properties.is.private": "Privé", + "torrents.properties.name": "Nom", + "torrents.properties.percentage": "% terminé", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "Taille", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Message du tracker", + "torrents.properties.upload.speed": "Vitesse d'Envoi", + "torrents.properties.upload.total": "Données envoyées", + "torrents.properties.seeds": "Sources", + "torrents.properties.peers": "Pairs", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Confirmez-vous la suppression {count, plural, =0 {d'aucun torrent} =1 {d'un torrent} other {de # torrents}} ?", + "torrents.remove.delete.data": "Supprimer les données", + "torrents.remove.error.no.torrents.selected": "Vous n'avez sélectionné aucun torrent.", + "torrents.remove": "Supprimer les torrents", + "torrents.set.tags.button.set": "Définir les tags", + "torrents.set.tags.heading": "Définir les tags", + "torrents.set.tags.enter.tags": "Saisissez les tags", + "torrents.set.trackers.button.set": "Définir les mouchards", + "torrents.set.trackers.heading": "Définir les mouchards", + "torrents.set.trackers.enter.tracker": "Entrez un tracker", + "torrents.set.trackers.loading.trackers": "Chargement des trackers...", + "torrents.sort.title": "Trier par", + "connection-interruption.heading": "Impossible de se connecter au client", + "status.diskusage.title": "Utilisation du disque", + "status.diskusage.used": "Utilisé", + "status.diskusage.free": "Disponible", + "status.diskusage.total": "Total", + "locale.language.auto": "Automatique", + "locale.language.translate": "Commencer à traduire", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Taxonomie du torrent", + "dependency.loading.transfer.rate.details": "Détails du taux de transfert des données", + "dependency.loading.transfer.history": "Historique des transferts de données", + "dependency.loading.torrent.list": "Liste des torrents" +} diff --git a/client/src/javascript/i18n/translations/he.json b/client/src/javascript/i18n/translations/he.json new file mode 100644 index 000000000..27901cc9e --- /dev/null +++ b/client/src/javascript/i18n/translations/he.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Add Torrent", + "actionbar.button.remove.torrent": "Remove Torrent", + "alert.torrent.add": "Successfully added {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Failed to add {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Successfully moved {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move.failed": "Failed to move {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Successfully removed {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Failed to remove {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Successfully saved settings.", + "auth.add.user": "Add User", + "auth.create.account": "Create Account", + "auth.create.an.account": "Create an account", + "auth.create.an.account.intro": "Welcome to Flood!", + "auth.current.user": "Current User", + "auth.error.username.empty": "Username cannot be empty.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "Log In", + "auth.login": "Login", + "auth.login.intro": "Log in to your account.", + "auth.password": "Password", + "auth.user.accounts": "User Accounts", + "auth.username": "Username", + "auth.admin": "Admin", + "auth.message.not.admin": "User is not Admin", + "button.add": "Add", + "button.cancel": "Cancel", + "button.close": "Close", + "button.download": "Download", + "button.no": "No", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Save Settings", + "button.save.feed": "Save", + "button.state.adding": "Adding...", + "button.yes": "Yes", + "button.new": "New", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Connection could not be verified.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Connectivity Issue", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "Add Download Rule", + "feeds.add.feed": "Add Feed", + "feeds.applicable.feed": "Applicable Feed", + "feeds.apply.tags": "Apply Tags", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Exclude Pattern", + "feeds.existing.feeds": "Existing Feeds", + "feeds.existing.rules": "Existing Rules", + "feeds.interval": "Interval", + "feeds.label": "Label", + "feeds.match.count": "{count, plural, =1 {# match} other {# matches}}", + "feeds.match.pattern": "Match Pattern", + "feeds.match": "Match", + "feeds.exclude": "Exclude", + "feeds.no.feeds.available": "No feeds available.", + "feeds.no.feeds.defined": "No feeds defined.", + "feeds.no.items.matching": "No items matching search term.", + "feeds.no.rules.defined": "No rules defined.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Select Feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start on load", + "feeds.tabs.download.rules": "Download Rules", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent Feeds", + "feeds.tags": "Tags", + "feeds.test.match": "Test Match Pattern", + "feeds.time.hr": "Hours", + "feeds.time.min": "Minutes", + "feeds.time.day": "Days", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Search term", + "feeds.search.term": "Search term", + "feeds.validation.invalid.regular.expression": "Invalid regular expression.", + "feeds.validation.must.select.feed": "You must select a feed.", + "feeds.validation.must.specify.destination": "You must specify a destination.", + "feeds.validation.must.specify.label": "You must specify a label.", + "feeds.validation.must.specify.valid.feed.url": "You must specify a valid feed URL.", + "feeds.validation.interval.not.positive": "The interval must be a positive integer.", + "feeds.browse.feeds": "Browse feeds", + "filesystem.empty.directory": "Empty directory.", + "filesystem.error.eacces": "Flood does not have permission to read this directory.", + "filesystem.error.enoent": "This path does not exist. It will be created.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "Fetching directory structure...", + "filesystem.parent.directory": "Parent Directory", + "filter.all": "All", + "filter.status.title": "Filter by Status", + "filter.status.downloading": "Downloading", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Complete", + "filter.status.active": "Active", + "filter.status.inactive": "Inactive", + "filter.status.error": "Error", + "filter.status.stopped": "Stopped", + "filter.status.checking": "Checking", + "filter.tracker.title": "Filter by Tracker", + "filter.tag.title": "Filter by Tag", + "filter.untagged": "Untagged", + "general.ago": "ago", + "general.at": "at", + "general.to": "to", + "general.of": "of", + "general.clipboard.copy": "Copy", + "general.clipboard.copied": "Copied", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.", + "mediainfo.fetching": "Fetching...", + "mediainfo.heading": "Mediainfo Output", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Finished Downloading", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Error Reported", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Clear All", + "notification.showing": "Showing", + "priority.dont.download": "Don't Download", + "priority.high": "High", + "priority.low": "Low", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Download Slots Global", + "settings.bandwidth.slots.download.label": "Download Slots Per Torrent", + "settings.bandwidth.slots.heading": "Slot Availability", + "settings.bandwidth.slots.upload.global.label": "Upload Slots Global", + "settings.bandwidth.slots.upload.label": "Upload Slots Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown Presets: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown Presets: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Global Download Rate Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Global Upload Rate Throttle", + "settings.bandwidth.transferrate.heading": "Transfer Rate Throttles", + "settings.connectivity.dht.label": "Enable DHT", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentralized Peer Discovery", + "settings.connectivity.incoming.heading": "Incoming Connections", + "settings.connectivity.ip.hostname.label": "Reported IP/Hostname", + "settings.connectivity.max.http.connections": "Maximum HTTP Connections", + "settings.connectivity.peer.exchange.label": "Enable Peer Exchange", + "settings.connectivity.peers.desired.label": "Peers Desired", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maximum Peers", + "settings.connectivity.peers.min.label": "Minimum Peers", + "settings.connectivity.peers.seeding.max.label": "Maximum Peers Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimum Peers Seeding", + "settings.connectivity.port.open.label": "Open Port", + "settings.connectivity.port.randomize.label": "Randomize Port", + "settings.connectivity.port.range.label": "Port Range", + "settings.resources.disk.check.hash.label": "Verify Hash on Completion", + "settings.resources.disk.download.location.label": "Default Download Directory", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maximum Open Files", + "settings.resources.memory.heading": "Memory", + "settings.resources.memory.max.label": "Max Memory Usage", + "settings.tabs.bandwidth": "Bandwidth", + "settings.tabs.connectivity": "Connectivity", + "settings.tabs.heading": "Settings", + "settings.tabs.resources": "Resources", + "settings.tabs.authentication": "Authentication", + "settings.tabs.userinterface": "User Interface", + "settings.tabs.diskusage": "Disk Usage", + "settings.tabs.about": "About", + "settings.ui.locale": "Locale", + "settings.ui.language": "Language", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent List Display", + "settings.ui.torrent.size": "Torrent Size", + "settings.ui.torrent.size.expanded": "Expanded View", + "settings.ui.torrent.size.condensed": "Condensed View", + "settings.ui.torrent.details.enabled": "Enabled", + "settings.ui.torrent.details.tags.placement": "In the expanded view, tags work best at the end of the list.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Show", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "About Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Settings", + "sidebar.button.speedlimits": "Speed Limits", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log Out", + "sidebar.search.placeholder": "Search torrents", + "sidebar.transferdata.downloaded": "Downloaded", + "sidebar.transferdata.uploaded": "Uploaded", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Unlimited", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "hr", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Add Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Destination", + "torrents.add.destination.placeholder": "Destination", + "torrents.add.heading": "Add Torrents", + "torrents.add.start.label": "Start Torrent", + "torrents.add.tab.file.browse": "or click to browse", + "torrents.add.tab.file.drop": "Drop some files here,", + "torrents.add.tab.file.title": "By File", + "torrents.add.tab.url.input.placeholder": "Torrent URL or Magnet Link", + "torrents.add.tab.url.title": "By URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "Use as Base Path", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Details", + "torrents.details.files": "Files", + "torrents.details.files.loading": "Loading file detail...", + "torrents.details.files.download.file": "{count, plural, =1 {Download File} other {Download Files}}", + "torrents.details.general.comment": "Comment", + "torrents.details.general.connected": "{connected} connected of {total}", + "torrents.details.general.date.added": "Added", + "torrents.details.general.date.created": "Creation Date", + "torrents.details.general.downloaded": "Downloaded", + "torrents.details.general.free.disk.space": "Free Disk Space", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "General", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfer", + "torrents.details.general.location": "Location", + "torrents.details.general.none": "None", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignored", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Scheduler", + "torrents.details.general.seeds": "Seeds", + "torrents.details.general.size": "Size", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Tracker Message", + "torrents.details.general.type.private": "Private", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "There is no peer data for this torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} selected file} other {{countElement} selected files}}", + "torrents.details.selected.files.set.priority": "Set Priority", + "torrents.details.trackers.no.data": "There is no tracker data for this torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Clear Filters", + "torrents.list.context.check.hash": "Check Hash", + "torrents.list.context.details": "Torrent Details", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Set Torrent Location", + "torrents.list.context.pause": "Pause", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "Priority", + "torrents.list.context.remove": "Remove", + "torrents.list.context.set.tags": "Set Tags", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stop", + "torrents.list.no.torrents": "No torrents to display.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "of", + "torrents.move.button.set.location": "Set Location", + "torrents.move.button.state.setting": "Setting...", + "torrents.move.data.label": "Move data", + "torrents.move.check_hash.label": "Check hash", + "torrents.move.heading": "Set Torrent Location", + "torrents.properties.date.added": "Added", + "torrents.properties.comment": "Comment", + "torrents.properties.creation.date": "Creation Date", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Download Speed", + "torrents.properties.download.total": "Downloaded", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Free Disk Space", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignore Scheduler", + "torrents.properties.is.private": "Private", + "torrents.properties.name": "Name", + "torrents.properties.percentage": "Percent Complete", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "File Size", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Tracker Message", + "torrents.properties.upload.speed": "Upload Speed", + "torrents.properties.upload.total": "Uploaded", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Are you sure you want to remove {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Delete data", + "torrents.remove.error.no.torrents.selected": "You haven't selected any torrents.", + "torrents.remove": "Remove Torrents", + "torrents.set.tags.button.set": "Set Tags", + "torrents.set.tags.heading": "Set Tags", + "torrents.set.tags.enter.tags": "Enter tags", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "Sort By", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Disk Usage", + "status.diskusage.used": "Used", + "status.diskusage.free": "Free", + "status.diskusage.total": "Total", + "locale.language.auto": "Automatic", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Details", + "dependency.loading.transfer.history": "Data Transfer History", + "dependency.loading.torrent.list": "Torrent List" +} diff --git a/client/src/javascript/i18n/translations/hu.json b/client/src/javascript/i18n/translations/hu.json new file mode 100644 index 000000000..d87e269f3 --- /dev/null +++ b/client/src/javascript/i18n/translations/hu.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Torrent indítása", + "actionbar.button.stop.torrent": "Torrent megállítása", + "actionbar.button.add.torrent": "Torrent hozzáadása", + "actionbar.button.remove.torrent": "Torrent eltávolítása", + "alert.torrent.add": "{countElement} {count, plural, =1 {torrent} other {torrentek}} sikeresen hozzáadva.", + "alert.torrent.add.failed": "{countElement} {count, plural, =1 {torrentet} other {torrenteket}} nem sikerült hozzáadni.", + "alert.torrent.move": "{countElement} {count, plural, one {} =1 {torrent} other {torrent}} sikeresen áthelyezve.", + "alert.torrent.move.failed": "{countElement} {count, plural, one {} =1 {torrentet} other {torrentet}} nem sikerült áthelyezni.", + "alert.torrent.remove": "{countElement} {count, plural, one {} =1 {torrent} other {torrent}} sikeresen eltávolítva.", + "alert.torrent.remove.failed": "{countElement} {count, plural, one {} =1 {torrent} other {torrent}} eltávolítása sikertelen.", + "alert.settings.saved": "Beállítások sikeresen elmentve.", + "auth.add.user": "Új felhasználó", + "auth.create.account": "Fiók létrehozása", + "auth.create.an.account": "Fiók létrehozása", + "auth.create.an.account.intro": "Üdvözöli a Flood!", + "auth.current.user": "Jelenlegi felhasználó", + "auth.error.username.empty": "A felhasználónév nem lehet üres.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "Bejelentkezés", + "auth.login": "Bejelentkezés", + "auth.login.intro": "Jelentkezz be a fiókodba.", + "auth.password": "Jelszó", + "auth.user.accounts": "Felhasználói fiókok", + "auth.username": "Felhasználónév", + "auth.admin": "Adminisztrátor", + "auth.message.not.admin": "A felhasználó nem Admin", + "button.add": "Hozzáadás", + "button.cancel": "Mégse", + "button.close": "Close", + "button.download": "Letöltés", + "button.no": "Nem", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Beállítások mentése", + "button.save.feed": "Mentés", + "button.state.adding": "Hozzáadás...", + "button.yes": "Igen", + "button.new": "Új", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "A kapcsolatot nem sikerült ellenőrizni.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Kapcsolódási hiba", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "Letöltési szabály hozzáadása", + "feeds.add.feed": "Hírcsatorna hozzáadása", + "feeds.applicable.feed": "Applicable Feed", + "feeds.apply.tags": "Címkék hozzáadása", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Kizárási minta", + "feeds.existing.feeds": "Rendelkezésre álló hírcsatornák", + "feeds.existing.rules": "Rendelkezésre álló szabályok", + "feeds.interval": "Intervallum", + "feeds.label": "Megnevezés", + "feeds.match.count": "{count, plural, one {} =1 {# egyezés} other {# egyezés}}", + "feeds.match.pattern": "Egyezési minta", + "feeds.match": "Egyezés", + "feeds.exclude": "Kivéve", + "feeds.no.feeds.available": "Nincs elérhető hírcsatorna.", + "feeds.no.feeds.defined": "Nincs megadva hírcsatorna.", + "feeds.no.items.matching": "A keresési feltételnek egyetlen elem sem felel meg.", + "feeds.no.rules.defined": "Nincs megadva szabály.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Csatorna kiválasztása", + "feeds.select.interval": "Intervallum", + "feeds.start.on.load": "Indítás azonnal", + "feeds.tabs.download.rules": "Letöltési szabályok", + "feeds.tabs.feeds": "Hírcsatornák", + "feeds.tabs.heading": "Torrent hírcsatornák", + "feeds.tags": "Címkék", + "feeds.test.match": "Egyezési minta tesztelése", + "feeds.time.hr": "Óra", + "feeds.time.min": "Perc", + "feeds.time.day": "Nap", + "feeds.torrent.destination": "Letöltési könyvár helye", + "feeds.url": "URL", + "feeds.search": "Keresési kifejezés", + "feeds.search.term": "Keresési kifejezés", + "feeds.validation.invalid.regular.expression": "Érvénytelen szabályos kifejezés.", + "feeds.validation.must.select.feed": "Választanod kell egy csatornát.", + "feeds.validation.must.specify.destination": "Meg kell adnod egy célkönyvtárat.", + "feeds.validation.must.specify.label": "Meg kell adnod egy nevet.", + "feeds.validation.must.specify.valid.feed.url": "Érvényes hírcsatorna URL-t adj meg.", + "feeds.validation.interval.not.positive": "Az intervallum pozitív egész szám kell legyen.", + "feeds.browse.feeds": "Hírcsatornák böngészése", + "filesystem.empty.directory": "Üres könyvtár.", + "filesystem.error.eacces": "A Flood-nak nincs jogosultsága olvasni ezt a könyvtárat.", + "filesystem.error.enoent": "Az elérési út nem létezik, így létre lesz hozva.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "Könyvtárszerkezet lekérdezése...", + "filesystem.parent.directory": "Szülőkönyvtár", + "filter.all": "Mind", + "filter.status.title": "Szűrés állapot szerint", + "filter.status.downloading": "Letöltés alatt", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Befejezett", + "filter.status.active": "Aktív", + "filter.status.inactive": "Inaktív", + "filter.status.error": "Hiba", + "filter.status.stopped": "Megállítva", + "filter.status.checking": "Ellenőrzés alatt", + "filter.tracker.title": "Szűrés Tracker alapján", + "filter.tag.title": "Szűrés Címke alapján", + "filter.untagged": "Címke nélküli", + "general.ago": "ezelőtt", + "general.at": "-kor", + "general.to": "to", + "general.of": "of", + "general.clipboard.copy": "Másolás", + "general.clipboard.copied": "Másolva", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "Hiba történt a mediainfo futtatásakor a szerveren. Ellenőrizd, hogy a mediainfo telepítve van és elérhető a Flood számára.", + "mediainfo.fetching": "Lekérés...", + "mediainfo.heading": "A Mediainfo válasza", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Letöltés befejeződött", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Hibajelzés", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Összes törlése", + "notification.showing": "Mutat", + "priority.dont.download": "Ne töltsd", + "priority.high": "Magas", + "priority.low": "Alacsony", + "priority.normal": "Normál", + "settings.bandwidth.slots.download.global.label": "Globális letöltési szál", + "settings.bandwidth.slots.download.label": "Letöltési szál per Torrent", + "settings.bandwidth.slots.heading": "Átviteli szálak meghatározása", + "settings.bandwidth.slots.upload.global.label": "Globális feltöltési szál", + "settings.bandwidth.slots.upload.label": "Feltöltési szál per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Legördülő lista értékei: Letöltés", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Legördülő lista értékei: Feltöltés", + "settings.bandwidth.transferrate.global.throttle.download": "Globális letöltési sebességkorlát", + "settings.bandwidth.transferrate.global.throttle.upload": "Globális feltöltési sebességkorlát", + "settings.bandwidth.transferrate.heading": "Adatátviteli sebességkorlátok", + "settings.connectivity.dht.label": "DHT engedélyezése", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentralizált Peer-ek felderítése", + "settings.connectivity.incoming.heading": "Bejövő kapcsolatok", + "settings.connectivity.ip.hostname.label": "Közzétett IP/Hostname", + "settings.connectivity.max.http.connections": "Max. HTTP kapcsolatok száma", + "settings.connectivity.peer.exchange.label": "Peer csere engedélyezése", + "settings.connectivity.peers.desired.label": "Kívánt Peer-ek száma", + "settings.connectivity.peers.heading": "Peer-ek", + "settings.connectivity.peers.max.label": "Max. Peer-ek száma", + "settings.connectivity.peers.min.label": "Min. Peer-ek száma", + "settings.connectivity.peers.seeding.max.label": "Seedeléskor max. Peer-ek száma", + "settings.connectivity.peers.seeding.min.label": "Seedeléskor min. Peer-ek száma", + "settings.connectivity.port.open.label": "Nyitott port", + "settings.connectivity.port.randomize.label": "Véletlenszerű port", + "settings.connectivity.port.range.label": "Port tartomány", + "settings.resources.disk.check.hash.label": "Hash ellenőrzés a letöltés végén", + "settings.resources.disk.download.location.label": "Alapértelmezett letöltési könyvtár", + "settings.resources.disk.heading": "Lemez", + "settings.resources.max.open.files": "Max. megnyitott fájlok száma", + "settings.resources.memory.heading": "Memória", + "settings.resources.memory.max.label": "Max. memóriahasználat", + "settings.tabs.bandwidth": "Sávszélesség", + "settings.tabs.connectivity": "Kapcsolat", + "settings.tabs.heading": "Beállítások", + "settings.tabs.resources": "Erőforrások", + "settings.tabs.authentication": "Azonosítás", + "settings.tabs.userinterface": "Felhasználói felület", + "settings.tabs.diskusage": "Lemezhasználat", + "settings.tabs.about": "Névjegy", + "settings.ui.locale": "Területi beállítások", + "settings.ui.language": "Nyelv", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent lista megjelenítése", + "settings.ui.torrent.size": "Torrent mérete", + "settings.ui.torrent.size.expanded": "Bővített nézet", + "settings.ui.torrent.size.condensed": "Szűkített nézet", + "settings.ui.torrent.details.enabled": "Engedélyezve", + "settings.ui.torrent.details.tags.placement": "Kibővített nézetben a címkék megjelenítése a lista végén ajánlott.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Megjelenítés", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "A Flood-ról", + "sidebar.button.feeds": "Hírcsatornák", + "sidebar.button.settings": "Beállítások", + "sidebar.button.speedlimits": "Sebességkorlát", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Kijelentkezés", + "sidebar.search.placeholder": "Torrentek keresése", + "sidebar.transferdata.downloaded": "Letöltve", + "sidebar.transferdata.uploaded": "Feltöltve", + "sidebar.speedlimits.download": "LETÖLTÉS", + "sidebar.speedlimits.upload": "FELTÖLTÉS", + "speed.unlimited": "Korlátlan", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "év", + "unit.time.week": "hét", + "unit.time.day": "nap", + "unit.time.hour": "óra", + "unit.time.minute": "p", + "unit.time.second": "mp", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Torrent hozzáadása", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Célkönyvtár", + "torrents.add.destination.placeholder": "Célkönyvtár", + "torrents.add.heading": "Torrentek hozzáadása", + "torrents.add.start.label": "Torrent indítása", + "torrents.add.tab.file.browse": "vagy kattints a böngészéshez", + "torrents.add.tab.file.drop": "Húzd ide a fájlokat,", + "torrents.add.tab.file.title": "Fájl alapján", + "torrents.add.tab.url.input.placeholder": "Torrent URL vagy Magnet Link", + "torrents.add.tab.url.title": "URL alapján", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "Torrentek", + "torrents.add.tags": "Címkék", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "Használd kiindulási pontként", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "Szüneteltetés", + "torrents.details.actions.start": "Indítás", + "torrents.details.actions.stop": "Leállítás", + "torrents.details.details": "Részletek", + "torrents.details.files": "Fájlok", + "torrents.details.files.loading": "Fájl adatainak betöltése...", + "torrents.details.files.download.file": "{count, plural, one {} =1 {Fájl letöltése} other {Fájlok letöltése}}", + "torrents.details.general.comment": "Megjegyzés", + "torrents.details.general.connected": "{connected} csatlakozva a {total}-ból/ből", + "torrents.details.general.date.added": "Hozzáadva", + "torrents.details.general.date.created": "Létrehozás dátuma", + "torrents.details.general.downloaded": "Letöltve", + "torrents.details.general.free.disk.space": "Szabad lemezterület", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Általános", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Átvitel", + "torrents.details.general.location": "Tartózkodási hely", + "torrents.details.general.none": "Nincs", + "torrents.details.general.peers": "Peer-ek", + "torrents.details.general.scheduler.ignored": "Mellőzve", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Ütemező", + "torrents.details.general.seeds": "Seed-ek", + "torrents.details.general.size": "Méret", + "torrents.details.general.tags": "Címkék", + "torrents.details.general.tracker.message": "Tracker üzenete", + "torrents.details.general.type.private": "Privát", + "torrents.details.general.type.public": "Nyilvános", + "torrents.details.general.type": "Típus", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Nincs elérhető peer információ ehhez a torrenthez.", + "torrents.details.peers": "Peer-ek", + "torrents.details.selected.files": "{count, plural, one {} =1 {{countElement} kiválasztott fájl} other {{countElement} kiválasztott fájl}}", + "torrents.details.selected.files.set.priority": "Prioritás beállítása", + "torrents.details.trackers.no.data": "Nincs elérhető tracker információ ehhez a torrenthez.", + "torrents.details.trackers.type": "Típus", + "torrents.details.trackers": "Tracker-ek", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Szűrök törlése", + "torrents.list.context.check.hash": "Hash ellenőrzése", + "torrents.list.context.details": "Torrent részletei", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Áthelyezés", + "torrents.list.context.pause": "Szüneteltetve", + "torrents.list.context.download": "Letöltés helyigépre", + "torrents.list.context.priority": "Prioritás", + "torrents.list.context.remove": "Törlés", + "torrents.list.context.set.tags": "Címke hozzárendelése", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "Indítás", + "torrents.list.context.stop": "Leállítás", + "torrents.list.no.torrents": "Nincs megjeleníthető torrent.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "of", + "torrents.move.button.set.location": "Set Location", + "torrents.move.button.state.setting": "Beállítás...", + "torrents.move.data.label": "Adat áthelyezése", + "torrents.move.check_hash.label": "Hash ellenőrzése", + "torrents.move.heading": "Torrent áthelyezése másik könyvtárba", + "torrents.properties.date.added": "Hozzáadva", + "torrents.properties.comment": "Megjegyzés", + "torrents.properties.creation.date": "Létrehozás dátuma", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Letöltési sebesség", + "torrents.properties.download.total": "Letöltve", + "torrents.properties.eta": "Becsült idő", + "torrents.properties.free.disk.space": "Szabad lemezterület", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ütemező figyelmen kívül hagyása", + "torrents.properties.is.private": "Privát", + "torrents.properties.name": "Név", + "torrents.properties.percentage": "Százalék készen", + "torrents.properties.ratio": "Arány", + "torrents.properties.size": "Fájlméret", + "torrents.properties.tags": "Címkék", + "torrents.properties.tracker.message": "Tracker üzenete", + "torrents.properties.upload.speed": "Feltöltési sebesség", + "torrents.properties.upload.total": "Feltöltve", + "torrents.properties.seeds": "Seed-ek", + "torrents.properties.peers": "Peer-ek", + "torrents.properties.trackers": "Tracker-ek", + "torrents.remove.are.you.sure": "Biztosan el akarod távolítani ezt a/az {count, plural, one {} =1 {# torrent-tet} other {# torrent-tet}}?", + "torrents.remove.delete.data": "Adat törlése", + "torrents.remove.error.no.torrents.selected": "Nem jelöltél ki egyetlen torrentet sem.", + "torrents.remove": "Torrent-ek eltávolítása", + "torrents.set.tags.button.set": "Címke hozzárendelése", + "torrents.set.tags.heading": "Címke hozzárendelése", + "torrents.set.tags.enter.tags": "Címke felvétele", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "Rendezés", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Lemezhasználat", + "status.diskusage.used": "Felhasznált", + "status.diskusage.free": "Szabad", + "status.diskusage.total": "Összesen", + "locale.language.auto": "Automatikus", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Értesítések", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Adatátviteli sebesség részletei", + "dependency.loading.transfer.history": "Adatátviteli előzmények", + "dependency.loading.torrent.list": "Torrent lista" +} diff --git a/client/src/javascript/i18n/translations/it.json b/client/src/javascript/i18n/translations/it.json new file mode 100644 index 000000000..cc878067e --- /dev/null +++ b/client/src/javascript/i18n/translations/it.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Avvia Torrent", + "actionbar.button.stop.torrent": "Ferma Torrent", + "actionbar.button.add.torrent": "Aggiungi Torrent", + "actionbar.button.remove.torrent": "Rimuovi Torrent", + "alert.torrent.add": "Hai aggiunto con successo {countElement} {count, plural, =1 {torrent} other {torrent}}", + "alert.torrent.add.failed": "Impossibile aggiungere {countElement} {count, plural, =1 {torrent} other {torrent}}", + "alert.torrent.move": "Spostato con successo {countElement} {count, plural, =1 {torrent} other {torrent}}", + "alert.torrent.move.failed": "Impossibile spostare {countElement} {count, plural, =1 {torrent} other {torrent}}", + "alert.torrent.remove": "Hai rimosso con successo {countElement} {count, plural, =1 {torrent} other {torrent}}", + "alert.torrent.remove.failed": "Impossibile rimuovere {countElement} {count, plural, =1 {torrent} other {torrent}}", + "alert.settings.saved": "Impostazioni salvate con successo.", + "auth.add.user": "Aggiungi Utente", + "auth.create.account": "Crea Account", + "auth.create.an.account": "Crea un account", + "auth.create.an.account.intro": "Benvenuto a Flood!", + "auth.current.user": "Utente Attuale", + "auth.error.username.empty": "Il nome utente non può essere vuoto.", + "auth.error.password.empty": "La password non può essere vuota.", + "auth.input.clear": "Clear", + "auth.log.in": "Log In", + "auth.login": "Accedi", + "auth.login.intro": "Accedi al tuo account.", + "auth.password": "Password", + "auth.user.accounts": "Account Utente", + "auth.username": "Username", + "auth.admin": "Amministratore", + "auth.message.not.admin": "L'utente non è amministratore", + "button.add": "Aggiungi", + "button.cancel": "Annulla", + "button.close": "Close", + "button.download": "Scarica", + "button.no": "No", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Salva Impostazioni", + "button.save.feed": "Salva", + "button.state.adding": "Aggiungi...", + "button.yes": "Sì", + "button.new": "Nuovo", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "La connessione non può essere verificata.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Le impostazioni di connessione non possono essere vuote.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Tipo Di Connessione", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname o IP", + "connection.settings.rtorrent.port": "Porta", + "connection.settings.rtorrent.port.input.placeholder": "Porta", + "connection.settings.rtorrent.socket": "Percorso", + "connection.settings.rtorrent.socket.input.placeholder": "Percorso del socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL a qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Problema Di Connettività", + "connectivity.modal.content": "Impossibile connettersi al client. Si prega di aggiornare le impostazioni di connessione.", + "feeds.add.automatic.download.rule": "Aggiungi Regola Download", + "feeds.add.feed": "Aggiungi Feed", + "feeds.applicable.feed": "Alimenti Applicabili", + "feeds.apply.tags": "Applica Etichette", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Escludi Motivo", + "feeds.existing.feeds": "Feed Esistenti", + "feeds.existing.rules": "Regole Esistenti", + "feeds.interval": "Intervallo", + "feeds.label": "Etichetta", + "feeds.match.count": "{count, plural, =1 {# partita} other {# partite}}", + "feeds.match.pattern": "Modello Di Partita", + "feeds.match": "Partita", + "feeds.exclude": "Escludi", + "feeds.no.feeds.available": "Nessun feed disponibile.", + "feeds.no.feeds.defined": "Nessun feed definito.", + "feeds.no.items.matching": "Nessun elemento corrispondente al termine di ricerca.", + "feeds.no.rules.defined": "Nessuna regola definita.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Seleziona Feed", + "feeds.select.interval": "Intervallo", + "feeds.start.on.load": "Avvia al caricamento", + "feeds.tabs.download.rules": "Scarica Regole", + "feeds.tabs.feeds": "Feed", + "feeds.tabs.heading": "Feed Torrent", + "feeds.tags": "Etichette", + "feeds.test.match": "Motivo Partita Di Prova", + "feeds.time.hr": "Ore", + "feeds.time.min": "Minuti", + "feeds.time.day": "Giorni", + "feeds.torrent.destination": "Destinazione Torrent", + "feeds.url": "URL", + "feeds.search": "Termine di ricerca", + "feeds.search.term": "Termine di ricerca", + "feeds.validation.invalid.regular.expression": "Espressione regolare non valida.", + "feeds.validation.must.select.feed": "Devi selezionare un feed.", + "feeds.validation.must.specify.destination": "È necessario specificare una destinazione.", + "feeds.validation.must.specify.label": "È necessario specificare un'etichetta.", + "feeds.validation.must.specify.valid.feed.url": "È necessario specificare un URL feed valido.", + "feeds.validation.interval.not.positive": "L'intervallo deve essere un numero intero positivo.", + "feeds.browse.feeds": "Sfoglia i feed", + "filesystem.empty.directory": "Directory vuota.", + "filesystem.error.eacces": "Flood non ha il permesso di leggere questa directory.", + "filesystem.error.enoent": "Questo percorso non esiste. Sarà creato.", + "filesystem.error.unknown": "Si è verificato un errore sconosciuto. Riprova.", + "filesystem.fetching": "Recupero struttura directory...", + "filesystem.parent.directory": "Cartella Superiore", + "filter.all": "Tutti", + "filter.status.title": "Filtra per stato", + "filter.status.downloading": "Scaricamento", + "filter.status.seeding": "Semina", + "filter.status.completed": "Complete", + "filter.status.active": "Attivo", + "filter.status.inactive": "Inattivo", + "filter.status.error": "Errore", + "filter.status.stopped": "Fermato", + "filter.status.checking": "Controllo", + "filter.tracker.title": "Filtra per Tracker", + "filter.tag.title": "Filtra per Tag", + "filter.untagged": "Untagged", + "general.ago": "fa", + "general.at": "a", + "general.to": "a", + "general.of": "di", + "general.clipboard.copy": "Copia", + "general.clipboard.copied": "Copiato", + "general.error.unknown": "Si è verificato un errore sconosciuto", + "mediainfo.execError": "Si è verificato un errore durante l'esecuzione di mediainfo sul server. Verificare che mediainfo sia installato e disponibile nel PATH to Flood.", + "mediainfo.fetching": "Recupero...", + "mediainfo.heading": "Uscita Mediainfo", + "notification.feed.torrent.added.heading": "Feed Item Accodato", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Scaricamento Finito", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Errore Segnalato", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Cancella Tutto", + "notification.showing": "Mostrando", + "priority.dont.download": "Non Scaricare", + "priority.high": "Alto", + "priority.low": "Basso", + "priority.normal": "Normale", + "settings.bandwidth.slots.download.global.label": "Scarica Slot Globali", + "settings.bandwidth.slots.download.label": "Scarica Slot Per Torrent", + "settings.bandwidth.slots.heading": "Disponibilità Slot", + "settings.bandwidth.slots.upload.global.label": "Carica Slot Globali", + "settings.bandwidth.slots.upload.label": "Carica Slot Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Preimpostazioni A Discesa: Scarica", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Preimpostazioni A Discesa: Carica", + "settings.bandwidth.transferrate.global.throttle.download": "Velocità Di Download Globale Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Acceleratore Globale Del Tasso Di Caricamento", + "settings.bandwidth.transferrate.heading": "Velocità Di Trasferimento Throttles", + "settings.connectivity.dht.label": "Abilita DHT", + "settings.connectivity.dht.port.label": "Porta DHT", + "settings.connectivity.dpd.heading": "Scoperta Peer Decentralizzata", + "settings.connectivity.incoming.heading": "Connessioni In Entrata", + "settings.connectivity.ip.hostname.label": "Ip/Hostname Segnalato", + "settings.connectivity.max.http.connections": "Connessioni HTTP Massime", + "settings.connectivity.peer.exchange.label": "Abilita Scambio Peer", + "settings.connectivity.peers.desired.label": "Pari Desiderati", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Peers Massimo", + "settings.connectivity.peers.min.label": "Peers Minimo", + "settings.connectivity.peers.seeding.max.label": "Massima Seeding Dei Peers", + "settings.connectivity.peers.seeding.min.label": "Seeding Dei Peers Minimi", + "settings.connectivity.port.open.label": "Porta Aperta", + "settings.connectivity.port.randomize.label": "Porta Casuale", + "settings.connectivity.port.range.label": "Intervallo Di Porta", + "settings.resources.disk.check.hash.label": "Verifica l'hash al completamento", + "settings.resources.disk.download.location.label": "Directory Di Download Predefinita", + "settings.resources.disk.heading": "Disco", + "settings.resources.max.open.files": "File Aperti Massimi", + "settings.resources.memory.heading": "Memoria", + "settings.resources.memory.max.label": "Utilizzo Massimo Della Memoria", + "settings.tabs.bandwidth": "Larghezza Di Banda", + "settings.tabs.connectivity": "Connettività", + "settings.tabs.heading": "Impostazioni", + "settings.tabs.resources": "Risorse", + "settings.tabs.authentication": "Autenticazione", + "settings.tabs.userinterface": "Interfaccia Utente", + "settings.tabs.diskusage": "Uso Del Disco", + "settings.tabs.about": "Informazioni", + "settings.ui.locale": "Localizzazione", + "settings.ui.language": "Lingua", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Visualizzazione Elenco Torrent", + "settings.ui.torrent.size": "Dimensione Torrent", + "settings.ui.torrent.size.expanded": "Vista Estesa", + "settings.ui.torrent.size.condensed": "Vista Condensata", + "settings.ui.torrent.details.enabled": "Abilitato", + "settings.ui.torrent.details.tags.placement": "Nella vista espansa, i tag funzionano meglio alla fine dell'elenco.", + "settings.ui.torrent.context.menu.items.show": "Mostra", + "settings.ui.displayed.details": "Colonne Dettagli Torrent", + "settings.ui.displayed.context.menu.items": "Elementi Del Menu Contestuale", + "settings.diskusage.show": "Mostra", + "settings.diskusage.mount.points": "Punti Di Montaggio Utilizzo Disco", + "settings.about.flood": "Informazioni Sull'Inondazione", + "sidebar.button.feeds": "Feed", + "sidebar.button.settings": "Impostazioni", + "sidebar.button.speedlimits": "Limiti Di Velocità", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Esci", + "sidebar.search.placeholder": "Cerca torrent", + "sidebar.transferdata.downloaded": "Scaricato", + "sidebar.transferdata.uploaded": "Caricato", + "sidebar.speedlimits.download": "SCARICA", + "sidebar.speedlimits.upload": "SPLOAD", + "speed.unlimited": "Illimitato", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "g", + "unit.time.hour": "ora", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Aggiungi Torrent", + "torrents.add.cookies.label": "Cookie", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Destinazione", + "torrents.add.destination.placeholder": "Destinazione", + "torrents.add.heading": "Aggiungi Torrent", + "torrents.add.start.label": "Avvia Torrent", + "torrents.add.tab.file.browse": "o clicca per sfogliare", + "torrents.add.tab.file.drop": "Trascina qui alcuni file,", + "torrents.add.tab.file.title": "Per File", + "torrents.add.tab.url.input.placeholder": "Url Torrent o Magnet Link", + "torrents.add.tab.url.title": "Per URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Crea", + "torrents.add.torrents.label": "Torrent", + "torrents.add.tags": "Etichette", + "torrents.create.source.path.label": "Fonte", + "torrents.create.trackers.label": "Tracker", + "torrents.create.tracker.input.placeholder": "Url Del Tracker", + "torrents.create.base.name.label": "Nome Base", + "torrents.create.base.name.input.placeholder": "File di base opzionale o nome di directory del torrent", + "torrents.create.comment.label": "Commento", + "torrents.create.comment.input.placeholder": "Commento opzionale nel file torrent", + "torrents.create.info.source.label": "Sorgente Informazioni", + "torrents.create.info.source.input.placeholder": "Voce sorgente opzionale in infohash", + "torrents.create.is.private.label": "Privato", + "torrents.create.tags.input.placeholder": "Tags in Flood. Non aggiunto al torrent creato.", + "torrents.destination.base_path": "Usa come percorso base", + "torrents.destination.completed": "Completato", + "torrents.details.actions.pause": "Pausa", + "torrents.details.actions.start": "Inizia", + "torrents.details.actions.stop": "Ferma", + "torrents.details.details": "Dettagli", + "torrents.details.files": "File", + "torrents.details.files.loading": "Caricamento dettagli del file...", + "torrents.details.files.download.file": "{count, plural, =1 {Scarica il file} other {Scarica i file}}", + "torrents.details.general.comment": "Commento", + "torrents.details.general.connected": "{connected} connesso da {total}", + "torrents.details.general.date.added": "Aggiunto", + "torrents.details.general.date.created": "Data Di Creazione", + "torrents.details.general.downloaded": "Scaricato", + "torrents.details.general.free.disk.space": "Spazio Libero Su Disco", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Generale", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Trasferisci", + "torrents.details.general.location": "Posizione", + "torrents.details.general.none": "Nessuno", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignorato", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Pianificatore", + "torrents.details.general.seeds": "Semi", + "torrents.details.general.size": "Dimensione", + "torrents.details.general.tags": "Etichette", + "torrents.details.general.tracker.message": "Messaggio Tracker", + "torrents.details.general.type.private": "Privato", + "torrents.details.general.type.public": "Pubblico", + "torrents.details.general.type": "Tipo", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Non ci sono dati peer per questo torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} file selezionato} other {{countElement} file selezionati}}", + "torrents.details.selected.files.set.priority": "Imposta Priorità", + "torrents.details.trackers.no.data": "Non ci sono dati tracker per questo torrent.", + "torrents.details.trackers.type": "Tipo", + "torrents.details.trackers": "Tracker", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Cancella Filtri", + "torrents.list.context.check.hash": "Controlla Hash", + "torrents.list.context.details": "Dettagli Torrent", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Imposta Posizione Del Torrent", + "torrents.list.context.pause": "Pausa", + "torrents.list.context.download": "Scarica", + "torrents.list.context.priority": "Priorità", + "torrents.list.context.remove": "Rimuovi", + "torrents.list.context.set.tags": "Imposta Etichette", + "torrents.list.context.set.trackers": "Imposta Tracker", + "torrents.list.context.start": "Inizia", + "torrents.list.context.stop": "Ferma", + "torrents.list.no.torrents": "Nessun torrent da visualizzare.", + "torrents.list.drop": "Trascina qui i file per aggiungerli.", + "torrents.list.cannot.connect": "Impossibile connettersi al client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "di", + "torrents.move.button.set.location": "Imposta Posizione", + "torrents.move.button.state.setting": "Impostazione...", + "torrents.move.data.label": "Sposta dati", + "torrents.move.check_hash.label": "Controlla hash", + "torrents.move.heading": "Imposta Posizione Del Torrent", + "torrents.properties.date.added": "Aggiunto", + "torrents.properties.comment": "Commento", + "torrents.properties.creation.date": "Data Di Creazione", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Velocità Di Download", + "torrents.properties.download.total": "Scaricato", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Spazio Libero Su Disco", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignora Scheduler", + "torrents.properties.is.private": "Privato", + "torrents.properties.name": "Nome", + "torrents.properties.percentage": "Percentuale Completata", + "torrents.properties.ratio": "Rapporto", + "torrents.properties.size": "Dimensione File", + "torrents.properties.tags": "Etichette", + "torrents.properties.tracker.message": "Messaggio Tracker", + "torrents.properties.upload.speed": "Velocità Di Caricamento", + "torrents.properties.upload.total": "Caricato", + "torrents.properties.seeds": "Semi", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Tracker", + "torrents.remove.are.you.sure": "Sei sicuro di voler rimuovere {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Elimina dati", + "torrents.remove.error.no.torrents.selected": "Non hai selezionato alcun torrent.", + "torrents.remove": "Rimuovi Torrent", + "torrents.set.tags.button.set": "Imposta Etichette", + "torrents.set.tags.heading": "Imposta Etichette", + "torrents.set.tags.enter.tags": "Inserisci tag", + "torrents.set.trackers.button.set": "Imposta Tracker", + "torrents.set.trackers.heading": "Imposta Tracker", + "torrents.set.trackers.enter.tracker": "Inserisci un tracker", + "torrents.set.trackers.loading.trackers": "Caricamento trackers...", + "torrents.sort.title": "Ordina Per", + "connection-interruption.heading": "Impossibile connettersi al client", + "status.diskusage.title": "Uso Del Disco", + "status.diskusage.used": "Usato", + "status.diskusage.free": "Gratis", + "status.diskusage.total": "Totale", + "locale.language.auto": "Automatico", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifiche", + "dependency.loading.torrent.taxonomy": "Tassonomia Torrent", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Dettagli", + "dependency.loading.transfer.history": "Cronologia Trasferimento Dati", + "dependency.loading.torrent.list": "Lista Torrent" +} diff --git a/client/src/javascript/i18n/translations/ja.json b/client/src/javascript/i18n/translations/ja.json new file mode 100644 index 000000000..01a06686a --- /dev/null +++ b/client/src/javascript/i18n/translations/ja.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Torrent を開始", + "actionbar.button.stop.torrent": "Torrent を停止", + "actionbar.button.add.torrent": "トレントを追加", + "actionbar.button.remove.torrent": "Torrent を削除", + "alert.torrent.add": "{countElement} を追加しました {count, plural, =1 {Torrent} other {Torrent}}", + "alert.torrent.add.failed": "{countElement} の追加に失敗しました {count, plural, =1 {Torrent} other {Torrent}}", + "alert.torrent.move": "{countElement} を移動しました {count, plural, =1 {Torrent} other {Torrent}}", + "alert.torrent.move.failed": "{countElement} の移動に失敗しました {count, plural, =1 {Torrent} other {Torrent}}", + "alert.torrent.remove": "{countElement} を削除しました {count, plural, =1 {Torrent} other {Torrent}}", + "alert.torrent.remove.failed": "{countElement} の削除に失敗しました {count, plural, =1 {Torrent} other {Torrent}}", + "alert.settings.saved": "設定を正常に保存しました。", + "auth.add.user": "ユーザーを追加", + "auth.create.account": "アカウントを作成", + "auth.create.an.account": "アカウントを作成", + "auth.create.an.account.intro": "洪水へようこそ!", + "auth.current.user": "現在のユーザー", + "auth.error.username.empty": "ユーザー名は空にできません。", + "auth.error.password.empty": "パスワードは空にできません。", + "auth.input.clear": "Clear", + "auth.log.in": "ログイン", + "auth.login": "ログイン", + "auth.login.intro": "アカウントにログインします。", + "auth.password": "パスワード", + "auth.user.accounts": "ユーザーアカウント", + "auth.username": "ユーザー名", + "auth.admin": "管理者", + "auth.message.not.admin": "ユーザーは管理者ではありません", + "button.add": "追加", + "button.cancel": "キャンセル", + "button.close": "Close", + "button.download": "ダウンロード", + "button.no": "いいえ", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "設定を保存", + "button.save.feed": "保存", + "button.state.adding": "追加中...", + "button.yes": "はい", + "button.new": "新規作成", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "接続を確認できませんでした。", + "connection.settings.client.select": "クライアント", + "connection.settings.error.empty": "接続設定は空にできません。", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "接続タイプ", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "ホスト", + "connection.settings.rtorrent.host.input.placeholder": "ホスト名または IP", + "connection.settings.rtorrent.port": "ポート", + "connection.settings.rtorrent.port.input.placeholder": "ポート", + "connection.settings.rtorrent.socket": "パス", + "connection.settings.rtorrent.socket.input.placeholder": "ソケットへのパス", + "connection.settings.qbittorrent": "qBittorrentformat@@0", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrentの Web API", + "connection.settings.qbittorrent.username": "ユーザー名", + "connection.settings.qbittorrent.username.input.placeholder": "ユーザー名", + "connection.settings.qbittorrent.password": "パスワード", + "connection.settings.qbittorrent.password.input.placeholder": "パスワード", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "接続の問題", + "connectivity.modal.content": "クライアントに接続できません。接続設定を更新してください。", + "feeds.add.automatic.download.rule": "ダウンロードルールを追加", + "feeds.add.feed": "フィードを追加", + "feeds.applicable.feed": "適用されるフィード", + "feeds.apply.tags": "タグを適用", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "パターンを除外", + "feeds.existing.feeds": "既存のフィード", + "feeds.existing.rules": "既存のルール", + "feeds.interval": "間隔", + "feeds.label": "ラベル", + "feeds.match.count": "{count, plural, =1 {# match} other {# match}}", + "feeds.match.pattern": "パターンに一致", + "feeds.match": "一致", + "feeds.exclude": "除外", + "feeds.no.feeds.available": "利用可能なフィードはありません。", + "feeds.no.feeds.defined": "フィードが定義されていません。", + "feeds.no.items.matching": "検索語に一致するアイテムはありません。", + "feeds.no.rules.defined": "ルールが定義されていません。", + "feeds.regEx": "RegEx", + "feeds.select.feed": "フィードを選択", + "feeds.select.interval": "間隔", + "feeds.start.on.load": "ロード時に開始", + "feeds.tabs.download.rules": "ルールをダウンロード", + "feeds.tabs.feeds": "フィード", + "feeds.tabs.heading": "トレントフィード", + "feeds.tags": "タグ", + "feeds.test.match": "テストマッチパターン", + "feeds.time.hr": "時間", + "feeds.time.min": "分", + "feeds.time.day": "日数", + "feeds.torrent.destination": "トレントの宛先", + "feeds.url": "URL", + "feeds.search": "検索キーワード:", + "feeds.search.term": "検索キーワード:", + "feeds.validation.invalid.regular.expression": "無効な正規表現です。", + "feeds.validation.must.select.feed": "フィードを選択してください。", + "feeds.validation.must.specify.destination": "目的地を指定する必要があります。", + "feeds.validation.must.specify.label": "ラベルを指定する必要があります。", + "feeds.validation.must.specify.valid.feed.url": "有効なフィードURLを指定する必要があります。", + "feeds.validation.interval.not.positive": "intervalは正の整数でなければなりません。", + "feeds.browse.feeds": "フィードを参照", + "filesystem.empty.directory": "空のディレクトリ。", + "filesystem.error.eacces": "このディレクトリを読み込む権限がありません。", + "filesystem.error.enoent": "このパスは存在しません。作成されます。", + "filesystem.error.unknown": "不明なエラーが発生しました。もう一度やり直してください。", + "filesystem.fetching": "ディレクトリ構造を取得しています...", + "filesystem.parent.directory": "親ディレクトリ", + "filter.all": "すべて", + "filter.status.title": "ステータスでフィルター", + "filter.status.downloading": "ダウンロード中", + "filter.status.seeding": "シード中", + "filter.status.completed": "Complete", + "filter.status.active": "アクティブ", + "filter.status.inactive": "非アクティブ", + "filter.status.error": "エラー", + "filter.status.stopped": "停止しました", + "filter.status.checking": "確認中", + "filter.tracker.title": "トラッカーでフィルター", + "filter.tag.title": "タグでフィルター", + "filter.untagged": "タグなし", + "general.ago": "前", + "general.at": "に", + "general.to": "to", + "general.of": "/", + "general.clipboard.copy": "コピー", + "general.clipboard.copied": "コピーしました", + "general.error.unknown": "不明なエラーが発生しました", + "mediainfo.execError": "サーバー上でmediainfoを実行中にエラーが発生しました。mediainfoがインストールされ、PATH to Floodで利用できることを確認してください。", + "mediainfo.fetching": "フェッチ中...", + "mediainfo.heading": "Mediainfo 出力", + "notification.feed.torrent.added.heading": "フィードアイテムがキューに追加されました", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "ダウンロード完了", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "報告エラー", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "すべてクリア", + "notification.showing": "表示", + "priority.dont.download": "ダウンロードしない", + "priority.high": "高い", + "priority.low": "低い", + "priority.normal": "標準", + "settings.bandwidth.slots.download.global.label": "グローバルスロットをダウンロード", + "settings.bandwidth.slots.download.label": "Torrent ごとにスロットをダウンロード", + "settings.bandwidth.slots.heading": "スロットの在庫状況", + "settings.bandwidth.slots.upload.global.label": "グローバルスロットをアップロード", + "settings.bandwidth.slots.upload.label": "Torrent ごとにスロットをアップロード", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "ドロップダウンプリセット: ダウンロード", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "ドロップダウンプリセット: アップロード", + "settings.bandwidth.transferrate.global.throttle.download": "グローバルダウンロードレートスロットル", + "settings.bandwidth.transferrate.global.throttle.upload": "グローバルアップロードレートスロットル", + "settings.bandwidth.transferrate.heading": "スロットル転送レート", + "settings.connectivity.dht.label": "DHTを有効にする", + "settings.connectivity.dht.port.label": "DHTポート", + "settings.connectivity.dpd.heading": "分散型ピアの発見", + "settings.connectivity.incoming.heading": "受信接続", + "settings.connectivity.ip.hostname.label": "報告されたIP/ホスト名", + "settings.connectivity.max.http.connections": "最大HTTP接続", + "settings.connectivity.peer.exchange.label": "ピア交換を有効にする", + "settings.connectivity.peers.desired.label": "ピアが望ましいです", + "settings.connectivity.peers.heading": "ピア", + "settings.connectivity.peers.max.label": "最大ピア数", + "settings.connectivity.peers.min.label": "ピアの最小数", + "settings.connectivity.peers.seeding.max.label": "ピアシードの最大数", + "settings.connectivity.peers.seeding.min.label": "ピアシードの最小数", + "settings.connectivity.port.open.label": "ポートを開く", + "settings.connectivity.port.randomize.label": "ポートをランダム化", + "settings.connectivity.port.range.label": "ポート範囲", + "settings.resources.disk.check.hash.label": "完了時にハッシュを確認", + "settings.resources.disk.download.location.label": "デフォルトのダウンロードディレクトリ", + "settings.resources.disk.heading": "ディスク", + "settings.resources.max.open.files": "最大開いているファイル", + "settings.resources.memory.heading": "メモリ", + "settings.resources.memory.max.label": "最大メモリ使用量", + "settings.tabs.bandwidth": "Bandwidth", + "settings.tabs.connectivity": "接続", + "settings.tabs.heading": "設定", + "settings.tabs.resources": "リソース", + "settings.tabs.authentication": "認証", + "settings.tabs.userinterface": "ユーザー インターフェイス", + "settings.tabs.diskusage": "ディスクの使用量", + "settings.tabs.about": "About", + "settings.ui.locale": "ロケール", + "settings.ui.language": "言語", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "トレントリストの表示", + "settings.ui.torrent.size": "トレントサイズ", + "settings.ui.torrent.size.expanded": "展開表示", + "settings.ui.torrent.size.condensed": "Condensed View", + "settings.ui.torrent.details.enabled": "有効", + "settings.ui.torrent.details.tags.placement": "展開されたビューでは、タグはリストの最後に最適に動作します。", + "settings.ui.torrent.context.menu.items.show": "表示", + "settings.ui.displayed.details": "トレントの詳細列", + "settings.ui.displayed.context.menu.items": "コンテキストメニューアイテム", + "settings.diskusage.show": "表示", + "settings.diskusage.mount.points": "ディスク使用量マウントポイント", + "settings.about.flood": "洪水について", + "sidebar.button.feeds": "フィード", + "sidebar.button.settings": "設定", + "sidebar.button.speedlimits": "速度制限", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "ログアウト", + "sidebar.search.placeholder": "Torrent を検索", + "sidebar.transferdata.downloaded": "ダウンロード済み", + "sidebar.transferdata.uploaded": "アップロードしました", + "sidebar.speedlimits.download": "ダウンロード", + "sidebar.speedlimits.upload": "アップロード", + "speed.unlimited": "無制限です", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "時間", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "トレントを追加", + "torrents.add.cookies.label": "Cookie", + "torrents.add.cookies.input.placeholder": "任意の cookie-name=cookie-value", + "torrents.add.destination.label": "保存先", + "torrents.add.destination.placeholder": "保存先", + "torrents.add.heading": "Torrent を追加", + "torrents.add.start.label": "Torrent を開始", + "torrents.add.tab.file.browse": "を選択します。", + "torrents.add.tab.file.drop": "ここにファイルをドロップ", + "torrents.add.tab.file.title": "ファイル順", + "torrents.add.tab.url.input.placeholder": "Torrent URL または Magnet リンク", + "torrents.add.tab.url.title": "URL順", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "作成", + "torrents.add.torrents.label": "Torrent", + "torrents.add.tags": "タグ", + "torrents.create.source.path.label": "ソース", + "torrents.create.trackers.label": "トラッカー", + "torrents.create.tracker.input.placeholder": "トラッカーURL", + "torrents.create.base.name.label": "ベース名", + "torrents.create.base.name.input.placeholder": "オプションのベースファイルまたはTorrent のディレクトリ名", + "torrents.create.comment.label": "コメント", + "torrents.create.comment.input.placeholder": "Torrent ファイルにコメントする", + "torrents.create.info.source.label": "情報ソース", + "torrents.create.info.source.input.placeholder": "infohash のオプションのソース エントリ", + "torrents.create.is.private.label": "非公開", + "torrents.create.tags.input.placeholder": "作成されたトレントに追加されていません。", + "torrents.destination.base_path": "ベースパスとして使用", + "torrents.destination.completed": "完了", + "torrents.details.actions.pause": "一時停止", + "torrents.details.actions.start": "開始", + "torrents.details.actions.stop": "停止", + "torrents.details.details": "詳細", + "torrents.details.files": "ファイル", + "torrents.details.files.loading": "ファイル詳細を読み込んでいます...", + "torrents.details.files.download.file": "{count, plural, =1 {ファイルをダウンロード} other {ファイルをダウンロード}}", + "torrents.details.general.comment": "コメント", + "torrents.details.general.connected": "{connected} の {total} が接続されました", + "torrents.details.general.date.added": "追加しました", + "torrents.details.general.date.created": "作成日", + "torrents.details.general.downloaded": "ダウンロード済み", + "torrents.details.general.free.disk.space": "空きディスク容量", + "torrents.details.general.hash": "ハッシュ", + "torrents.details.general.heading.general": "全般", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "トラッカー", + "torrents.details.general.heading.transfer": "送金", + "torrents.details.general.location": "場所", + "torrents.details.general.none": "なし", + "torrents.details.general.peers": "ピア", + "torrents.details.general.scheduler.ignored": "無視", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "スケジューラ", + "torrents.details.general.seeds": "種", + "torrents.details.general.size": "サイズ", + "torrents.details.general.tags": "タグ", + "torrents.details.general.tracker.message": "トラッカーメッセージ", + "torrents.details.general.type.private": "非公開", + "torrents.details.general.type.public": "公開", + "torrents.details.general.type": "タイプ", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "このトレントのピアデータはありません。", + "torrents.details.peers": "ピア", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} 選択したファイル} other {{countElement} 選択したファイル}}", + "torrents.details.selected.files.set.priority": "優先度を設定", + "torrents.details.trackers.no.data": "このトレントにはトラッカーデータがありません。", + "torrents.details.trackers.type": "タイプ", + "torrents.details.trackers": "トラッカー", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "フィルタをクリア", + "torrents.list.context.check.hash": "ハッシュを確認", + "torrents.list.context.details": "トレントの詳細", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "トレントの場所を設定", + "torrents.list.context.pause": "一時停止", + "torrents.list.context.download": "ダウンロード", + "torrents.list.context.priority": "優先度", + "torrents.list.context.remove": "削除", + "torrents.list.context.set.tags": "タグを設定", + "torrents.list.context.set.trackers": "トラッカーの設定", + "torrents.list.context.start": "開始", + "torrents.list.context.stop": "停止", + "torrents.list.no.torrents": "表示するトレントがありません。", + "torrents.list.drop": "ここにファイルをドロップして追加します。", + "torrents.list.cannot.connect": "クライアントに接続できません。", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "/", + "torrents.move.button.set.location": "場所を設定", + "torrents.move.button.state.setting": "設定中...", + "torrents.move.data.label": "データを移動", + "torrents.move.check_hash.label": "ハッシュをチェック", + "torrents.move.heading": "トレントの場所を設定", + "torrents.properties.date.added": "追加しました", + "torrents.properties.comment": "コメント", + "torrents.properties.creation.date": "作成日", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "ダウンロード速度", + "torrents.properties.download.total": "ダウンロード済み", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "空きディスク容量", + "torrents.properties.hash": "ハッシュ", + "torrents.properties.ignore.schedule": "スケジューラを無視", + "torrents.properties.is.private": "非公開", + "torrents.properties.name": "名前", + "torrents.properties.percentage": "完了率", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "ファイルサイズ", + "torrents.properties.tags": "タグ", + "torrents.properties.tracker.message": "トラッカーメッセージ", + "torrents.properties.upload.speed": "アップロード速度", + "torrents.properties.upload.total": "アップロードしました", + "torrents.properties.seeds": "種", + "torrents.properties.peers": "ピア", + "torrents.properties.trackers": "トラッカー", + "torrents.remove.are.you.sure": "削除してもよろしいですか? {count, plural, =1 {# Torrent} other {# Torrent}}?", + "torrents.remove.delete.data": "データを削除", + "torrents.remove.error.no.torrents.selected": "トレントを選択していません。", + "torrents.remove": "Torrent を削除", + "torrents.set.tags.button.set": "タグを設定", + "torrents.set.tags.heading": "タグを設定", + "torrents.set.tags.enter.tags": "タグを入力", + "torrents.set.trackers.button.set": "トラッカーの設定", + "torrents.set.trackers.heading": "トラッカーの設定", + "torrents.set.trackers.enter.tracker": "トラッカーを入力してください", + "torrents.set.trackers.loading.trackers": "トラッカーを読み込み中...", + "torrents.sort.title": "並び替え", + "connection-interruption.heading": "クライアントに接続できません", + "status.diskusage.title": "ディスクの使用量", + "status.diskusage.used": "使用中", + "status.diskusage.free": "無料", + "status.diskusage.total": "合計", + "locale.language.auto": "自動", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "通知", + "dependency.loading.torrent.taxonomy": "トレント分類", + "dependency.loading.transfer.rate.details": "データ転送速度の詳細", + "dependency.loading.transfer.history": "データ転送履歴", + "dependency.loading.torrent.list": "Torrent リスト" +} diff --git a/client/src/javascript/i18n/translations/ko.json b/client/src/javascript/i18n/translations/ko.json new file mode 100644 index 000000000..4e57b0d1c --- /dev/null +++ b/client/src/javascript/i18n/translations/ko.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "토렌트 시작", + "actionbar.button.stop.torrent": "토렌트 중지", + "actionbar.button.add.torrent": "토렌트 추가", + "actionbar.button.remove.torrent": "토렌트 제거", + "alert.torrent.add": "{countElement} {count, plural, =1 {토렌트} other {토렌트}}를 추가했습니다.", + "alert.torrent.add.failed": "{countElement} {count, plural, =1 {토렌트} other {토렌트}}를 추가하지 못했습니다.", + "alert.torrent.move": "{countElement} {count, plural, =1 {토렌트} other {토렌트}}를 이동했습니다.", + "alert.torrent.move.failed": "{countElement} {count, plural, =1 {토렌트} other {토렌트}}를 이동하지 못했습니다.", + "alert.torrent.remove": "{countElement} {count, plural, =1 {토렌트} other {토렌트}}를 제거했습니다.", + "alert.torrent.remove.failed": "{countElement} {count, plural, =1 {토렌트} other {토렌트}}를 제거하지 못했습니다.", + "alert.settings.saved": "설정을 저장했습니다.", + "auth.add.user": "사용자 추가", + "auth.create.account": "계정 생성", + "auth.create.an.account": "계정 생성", + "auth.create.an.account.intro": "환영합니다, Flood입니다!", + "auth.current.user": "현재 사용자", + "auth.error.username.empty": "사용자 이름은 비어있을 수 없습니다.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "로그인", + "auth.login": "로그인", + "auth.login.intro": "Log in to your account.", + "auth.password": "비밀번호", + "auth.user.accounts": "사용자 계정", + "auth.username": "사용자 이름", + "auth.admin": "관리자", + "auth.message.not.admin": "관리자가 아닌 사용자입니다", + "button.add": "추가", + "button.cancel": "취소", + "button.close": "Close", + "button.download": "Download", + "button.no": "아니오", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "설정 저장", + "button.save.feed": "Save", + "button.state.adding": "추가 중...", + "button.yes": "예", + "button.new": "New", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "연결을 검증하지 못했습니다.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "연결 문제", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "다운로드 규칙 추가", + "feeds.add.feed": "피드 추가", + "feeds.applicable.feed": "가용 피드", + "feeds.apply.tags": "태그 적용", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "제외 패턴", + "feeds.existing.feeds": "설정된 피드", + "feeds.existing.rules": "설정된 규칙", + "feeds.interval": "Interval", + "feeds.label": "라벨", + "feeds.match.count": "{count, plural, =1 {# 일치} other {# 일치}}", + "feeds.match.pattern": "일치 패턴", + "feeds.match": "일치", + "feeds.exclude": "제외", + "feeds.no.feeds.available": "사용할 수 있는 피드가 없습니다.", + "feeds.no.feeds.defined": "지정된 피드가 없습니다.", + "feeds.no.items.matching": "No items matching search term.", + "feeds.no.rules.defined": "지정된 규칙이 없습니다.", + "feeds.regEx": "정규 표현식", + "feeds.select.feed": "피드 선택", + "feeds.select.interval": "간격", + "feeds.start.on.load": "시작 시 불러오기", + "feeds.tabs.download.rules": "다운로드 규칙", + "feeds.tabs.feeds": "피드", + "feeds.tabs.heading": "토렌트 피드", + "feeds.tags": "태그", + "feeds.test.match": "Test Match Pattern", + "feeds.time.hr": "{durationValue} 시간", + "feeds.time.min": "{durationValue} 분", + "feeds.time.day": "Days", + "feeds.torrent.destination": "토렌트 저장 경로", + "feeds.url": "URL", + "feeds.search": "Search term", + "feeds.search.term": "Search term", + "feeds.validation.invalid.regular.expression": "정규 표현식이 잘못되었습니다.", + "feeds.validation.must.select.feed": "피드를 선택해야 합니다.", + "feeds.validation.must.specify.destination": "저장할 경로를 지정해야 합니다.", + "feeds.validation.must.specify.label": "라벨을 지정해야 합니다.", + "feeds.validation.must.specify.valid.feed.url": "올바른 피드 URL을 지정해야 합니다.", + "feeds.validation.interval.not.positive": "The interval must be a positive integer.", + "feeds.browse.feeds": "Browse feeds", + "filesystem.empty.directory": "비어있는 디렉토리입니다.", + "filesystem.error.eacces": "Flood에 이 디렉토리를 읽을 권한이 없습니다.", + "filesystem.error.enoent": "없는 경로입니다. 생성될 것입니다.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "디렉토리 구조 가져오는 중...", + "filesystem.parent.directory": "Parent Directory", + "filter.all": "전체", + "filter.status.title": "필터: 상태", + "filter.status.downloading": "다운로드 중", + "filter.status.seeding": "Seeding", + "filter.status.completed": "완료", + "filter.status.active": "활성", + "filter.status.inactive": "비활성", + "filter.status.error": "오류", + "filter.status.stopped": "중지", + "filter.status.checking": "Checking", + "filter.tracker.title": "필터: 트래커", + "filter.tag.title": "필터: 태그", + "filter.untagged": "태그 없음", + "general.ago": "전", + "general.at": "@", + "general.to": "-", + "general.of": "/", + "general.clipboard.copy": "복사", + "general.clipboard.copied": "복사됨", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "서버에서 미디어 정보를 가져오는 중 오류가 발생했습니다. mediainfo가 설치되어 있으며 Flood가 쓸 수 있도록 PATH가 지정되어 있는지 확인하세요.", + "mediainfo.fetching": "가져오는 중...", + "mediainfo.heading": "Mediainfo 출력", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "다운로드 완료", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "오류 보고", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "모두 지우기", + "notification.showing": "표시 중", + "priority.dont.download": "다운로드하지 않음", + "priority.high": "높음", + "priority.low": "낮음", + "priority.normal": "보통", + "settings.bandwidth.slots.download.global.label": "전체 다운로드 슬롯 개수", + "settings.bandwidth.slots.download.label": "토렌트당 다운로드 슬롯 개수", + "settings.bandwidth.slots.heading": "가용 슬롯", + "settings.bandwidth.slots.upload.global.label": "전체 업로드 슬롯 개수", + "settings.bandwidth.slots.upload.label": "토렌트당 업로드 슬롯 개수", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "드롭다운 메뉴 속도 목록: 다운로드", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "드롭다운 메뉴 속도 목록: 업로드", + "settings.bandwidth.transferrate.global.throttle.download": "전체 다운로드 속도 제한", + "settings.bandwidth.transferrate.global.throttle.upload": "전체 업로드 속도 제한", + "settings.bandwidth.transferrate.heading": "전송 속도 제한", + "settings.connectivity.dht.label": "DHT 사용", + "settings.connectivity.dht.port.label": "DHT 포트", + "settings.connectivity.dpd.heading": "탈중앙 피어 탐색", + "settings.connectivity.incoming.heading": "들어오는 연결", + "settings.connectivity.ip.hostname.label": "보고된 IP/호스트명", + "settings.connectivity.max.http.connections": "최대 HTTP 연결", + "settings.connectivity.peer.exchange.label": "피어 교환 사용", + "settings.connectivity.peers.desired.label": "희망 피어 수", + "settings.connectivity.peers.heading": "피어", + "settings.connectivity.peers.max.label": "최대 피어 수", + "settings.connectivity.peers.min.label": "최소 피어 수", + "settings.connectivity.peers.seeding.max.label": "시딩할 최대 피어 수", + "settings.connectivity.peers.seeding.min.label": "시딩할 최소 피어 수", + "settings.connectivity.port.open.label": "포트 열기", + "settings.connectivity.port.randomize.label": "임의 포트", + "settings.connectivity.port.range.label": "포트 범위", + "settings.resources.disk.check.hash.label": "완료 시 해시 검증", + "settings.resources.disk.download.location.label": "기본 다운로드 경로", + "settings.resources.disk.heading": "디스크", + "settings.resources.max.open.files": "최대 열어둘 파일 개수", + "settings.resources.memory.heading": "메모리", + "settings.resources.memory.max.label": "최대 메모리 사용량", + "settings.tabs.bandwidth": "대역폭", + "settings.tabs.connectivity": "연결", + "settings.tabs.heading": "설정", + "settings.tabs.resources": "자원", + "settings.tabs.authentication": "인증", + "settings.tabs.userinterface": "사용자 인터페이스", + "settings.tabs.diskusage": "Disk Usage", + "settings.tabs.about": "About", + "settings.ui.locale": "지역", + "settings.ui.language": "언어", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "토렌트 목록 표시", + "settings.ui.torrent.size": "토렌트 크기", + "settings.ui.torrent.size.expanded": "확장 보기", + "settings.ui.torrent.size.condensed": "간략히 보기", + "settings.ui.torrent.details.enabled": "표시", + "settings.ui.torrent.details.tags.placement": "확장 보기에서는, 태그를 목록 끝에 배치하면 제일 잘 작동합니다.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Show", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "About Flood", + "sidebar.button.feeds": "피드", + "sidebar.button.settings": "설정", + "sidebar.button.speedlimits": "속도 제한", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "로그아웃", + "sidebar.search.placeholder": "토렌트 검색", + "sidebar.transferdata.downloaded": "다운로드한 크기", + "sidebar.transferdata.uploaded": "업로드한 크기", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "무제한", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "년", + "unit.time.week": "주", + "unit.time.day": "일", + "unit.time.hour": "시", + "unit.time.minute": "분", + "unit.time.second": "초", + "unit.time.infinity": "∞", + "torrents.add.button.add": "토렌트 추가", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "저장 경로", + "torrents.add.destination.placeholder": "저장 경로", + "torrents.add.heading": "토렌트 추가", + "torrents.add.start.label": "토렌트 시작", + "torrents.add.tab.file.browse": "또는 클릭해서 파일 열기", + "torrents.add.tab.file.drop": "파일을 여기로 끌어넣거나,", + "torrents.add.tab.file.title": "파일", + "torrents.add.tab.url.input.placeholder": "토렌트 URL 또는 마그넷 주소", + "torrents.add.tab.url.title": "By URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "토렌트", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "기본 경로로 사용", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "일시중지", + "torrents.details.actions.start": "시작", + "torrents.details.actions.stop": "중지", + "torrents.details.details": "속성", + "torrents.details.files": "파일", + "torrents.details.files.loading": "파일 목록 불러오는 중...", + "torrents.details.files.download.file": "{count, plural, =1 {선택한 파일 다운로드} other {선택한 파일 다운로드}}", + "torrents.details.general.comment": "설명", + "torrents.details.general.connected": "{total} 중 {connected} 연결됨", + "torrents.details.general.date.added": "추가한 날", + "torrents.details.general.date.created": "만든 날", + "torrents.details.general.downloaded": "다운로드한 크기", + "torrents.details.general.free.disk.space": "남은 디스크 공간", + "torrents.details.general.hash": "해시", + "torrents.details.general.heading.general": "일반", + "torrents.details.general.heading.torrent": "토렌트", + "torrents.details.general.heading.tracker": "트래커", + "torrents.details.general.heading.transfer": "전송", + "torrents.details.general.location": "다운로드 경로", + "torrents.details.general.none": "없음", + "torrents.details.general.peers": "피어", + "torrents.details.general.scheduler.ignored": "무시", + "torrents.details.general.scheduler.obeyed": "준수", + "torrents.details.general.scheduler": "스케줄러", + "torrents.details.general.seeds": "시드", + "torrents.details.general.size": "크기", + "torrents.details.general.tags": "태그", + "torrents.details.general.tracker.message": "트래커 메시지", + "torrents.details.general.type.private": "비공개", + "torrents.details.general.type.public": "공개", + "torrents.details.general.type": "종류", + "torrents.details.mediainfo": "미디어 정보", + "torrents.details.peers.no.data": "이 토렌트에는 피어 정보가 없습니다.", + "torrents.details.peers": "피어", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} 파일 선택} other {{countElement} 파일 선택}}", + "torrents.details.selected.files.set.priority": "우선순위 지정", + "torrents.details.trackers.no.data": "이 토렌트에는 트래커 정보가 없습니다.", + "torrents.details.trackers.type": "종류", + "torrents.details.trackers": "트래커", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "필터 지우기", + "torrents.list.context.check.hash": "해시 검사", + "torrents.list.context.details": "토렌트 속성", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "토렌트 위치 지정", + "torrents.list.context.pause": "일시중지", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "우선순위", + "torrents.list.context.remove": "제거", + "torrents.list.context.set.tags": "태그 지정", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "시작", + "torrents.list.context.stop": "중지", + "torrents.list.no.torrents": "표시할 토렌트가 없습니다.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{total} {of} {connected}", + "torrent.list.peers.of": "중", + "torrents.move.button.set.location": "경로 지정", + "torrents.move.button.state.setting": "이동 중...", + "torrents.move.data.label": "데이터 이동", + "torrents.move.check_hash.label": "Check hash", + "torrents.move.heading": "토렌트 경로 지정", + "torrents.properties.date.added": "추가한 날", + "torrents.properties.comment": "설명", + "torrents.properties.creation.date": "만든 날", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "다운로드 속도", + "torrents.properties.download.total": "다운로드한 크기", + "torrents.properties.eta": "예상 남은 시간", + "torrents.properties.free.disk.space": "남은 디스크 공간", + "torrents.properties.hash": "해시", + "torrents.properties.ignore.schedule": "스케줄러 무시", + "torrents.properties.is.private": "비공개", + "torrents.properties.name": "이름", + "torrents.properties.percentage": "완료율", + "torrents.properties.ratio": "비율", + "torrents.properties.size": "파일 크기", + "torrents.properties.tags": "태그", + "torrents.properties.tracker.message": "트래커 메시지", + "torrents.properties.upload.speed": "업로드 속도", + "torrents.properties.upload.total": "업로드 크기", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "정말 {count, plural, =0 {0개 토렌트를} =1 {토렌트 한 개를} other {#개 토렌트를}} 제거할까요?", + "torrents.remove.delete.data": "데이터 삭제", + "torrents.remove.error.no.torrents.selected": "토렌트를 선택하지 않았습니다.", + "torrents.remove": "토렌트 제거", + "torrents.set.tags.button.set": "태그 지정", + "torrents.set.tags.heading": "태그 지정", + "torrents.set.tags.enter.tags": "태그 입력", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "정렬 기준", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Disk Usage", + "status.diskusage.used": "Used", + "status.diskusage.free": "Free", + "status.diskusage.total": "Total", + "locale.language.auto": "자동", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Details", + "dependency.loading.transfer.history": "Data Transfer History", + "dependency.loading.torrent.list": "Torrent List" +} diff --git a/client/src/javascript/i18n/translations/nl.json b/client/src/javascript/i18n/translations/nl.json new file mode 100644 index 000000000..c33426ad6 --- /dev/null +++ b/client/src/javascript/i18n/translations/nl.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Torrent toevoegen", + "actionbar.button.remove.torrent": "Verwijder Torrent", + "alert.torrent.add": "Het toevoegen van {countElement} {count, plural, =1 {torrent} other {torrents}} is klaar.", + "alert.torrent.add.failed": "Het toevoegen van {countElement} {count, plural, =1 {torrent} other {torrents}} is mislukt.", + "alert.torrent.move": "Het verplaatsen van {countElement} {count, plural, =1 {torrent} other {torrents}} is klaar.", + "alert.torrent.move.failed": "Het verplaatsen van {countElement} {count, plural, =1 {torrent} other {torrents}} is mislukt.", + "alert.torrent.remove": "Het verwijderen van {countElement} {count, plural, =1 {torrent} other {torrents}} is klaar.", + "alert.torrent.remove.failed": "Het verwijderen van {countElement} {count, plural, =1 {torrent} other {torrents}} is mislukt.", + "alert.settings.saved": "Instellingen zijn opgeslagen.", + "auth.add.user": "Gebruiker toevoegen", + "auth.create.account": "Maak account", + "auth.create.an.account": "Maak een account", + "auth.create.an.account.intro": "Welkom bij Flood!", + "auth.current.user": "Huidige gebruiker", + "auth.error.username.empty": "Gebruikersnaam mag niet leeg zijn.", + "auth.error.password.empty": "Wachtwoord mag niet leeg zijn.", + "auth.input.clear": "Wissen", + "auth.log.in": "Log in", + "auth.login": "Aanmelden", + "auth.login.intro": "Log in op uw account.", + "auth.password": "Wachtwoord", + "auth.user.accounts": "Accounts", + "auth.username": "Gebruikersnaam", + "auth.admin": "Beheerder", + "auth.message.not.admin": "Gebruiker is geen Beheerder", + "button.add": "Toevoegen", + "button.cancel": "Annuleren", + "button.close": "Close", + "button.download": "downloaden", + "button.no": "Nee", + "button.ok": "OK", + "button.retry": "Opnieuw", + "button.save": "Instellingen opslaan", + "button.save.feed": "Opslaan", + "button.state.adding": "Toevoegen...", + "button.yes": "Ja", + "button.new": "Nieuw", + "connection-interruption.action.selection.retry": "Probeer opnieuw met huidige clientverbindinginstellingen", + "connection-interruption.action.selection.config": "Update clientverbindingsinstellingen", + "connection-interruption.not.admin": "Neem contact op met uw Flood beheerder als dit zo blijft.", + "connection-interruption.verification-error": "Verbinding kon niet worden geverifieerd.", + "connection.settings.client.select": "Klant", + "connection.settings.error.empty": "Verbindingsinstellingen kunnen niet leeg zijn.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Type verbinding", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Het blootstellen van rTorrent via TCP kan leiden tot privilege-escalatie.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Hostnaam", + "connection.settings.rtorrent.host.input.placeholder": "Hostnaam of IP", + "connection.settings.rtorrent.port": "Poort", + "connection.settings.rtorrent.port.input.placeholder": "Poort", + "connection.settings.rtorrent.socket": "Pad", + "connection.settings.rtorrent.socket.input.placeholder": "Pad naar socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL naar qBittorrent Web API", + "connection.settings.qbittorrent.username": "Gebruikersnaam", + "connection.settings.qbittorrent.username.input.placeholder": "Gebruikersnaam", + "connection.settings.qbittorrent.password": "Wachtwoord", + "connection.settings.qbittorrent.password.input.placeholder": "Wachtwoord", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL naar Transmission RPC interface", + "connection.settings.transmission.username": "Gebruikersnaam", + "connection.settings.transmission.username.input.placeholder": "Gebruikersnaam", + "connection.settings.transmission.password": "Wachtwoord", + "connection.settings.transmission.password.input.placeholder": "Wachtwoord", + "connectivity.modal.title": "Probleem met verbinding", + "connectivity.modal.content": "Kan geen verbinding maken met de client. Werk de verbindingsinstellingen bij.", + "feeds.add.automatic.download.rule": "Regel toevoegen", + "feeds.add.feed": "Feed toevoegen", + "feeds.applicable.feed": "Feed", + "feeds.apply.tags": "Tags toevoegen", + "feeds.check": "Valideer de regel door het uit te proberen. Niet opgeslagen of verzonden.", + "feeds.exclude.pattern": "Overslaan op patroon", + "feeds.existing.feeds": "Bestaande feeds", + "feeds.existing.rules": "Regels", + "feeds.interval": "Interval", + "feeds.label": "Omschrijving", + "feeds.match.count": "{count, plural, =1 {# match} other {# matches}}", + "feeds.match.pattern": "Overeenkomst op patroon", + "feeds.match": "Overeenkomst", + "feeds.exclude": "Uitsluiten", + "feeds.no.feeds.available": "Geen feeds beschikbaar.", + "feeds.no.feeds.defined": "Geen feeds toegevoegd.", + "feeds.no.items.matching": "Geen items die overeenkomen met de zoekterm.", + "feeds.no.rules.defined": "Geen regels toegevoegd.", + "feeds.regEx": "Reguliere expressie", + "feeds.select.feed": "Selecteer feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start na toevoegen", + "feeds.tabs.download.rules": "Regels", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent feeds", + "feeds.tags": "Labels", + "feeds.test.match": "Test Match Patroon", + "feeds.time.hr": "{durationValue} uur", + "feeds.time.min": "{durationValue} minuten", + "feeds.time.day": "dagen", + "feeds.torrent.destination": "Torrent downloadlocatie", + "feeds.url": "URL", + "feeds.search": "Zoek term", + "feeds.search.term": "Zoek term", + "feeds.validation.invalid.regular.expression": "Fout in reguliere expressie.", + "feeds.validation.must.select.feed": "Je moet een feed selecteren.", + "feeds.validation.must.specify.destination": "Je moet een downloadlocatie opgeven.", + "feeds.validation.must.specify.label": "Je moet een label selecteren.", + "feeds.validation.must.specify.valid.feed.url": "Je moet een geldige feed URL opgeven.", + "feeds.validation.interval.not.positive": "Je moet een positief nummer opgeven.", + "feeds.browse.feeds": "Bekijk feeds", + "filesystem.empty.directory": "Lege map.", + "filesystem.error.eacces": "Flood heeft geen toestemming om deze map te lezen.", + "filesystem.error.enoent": "Dit pad bestaat niet. Het zal worden aangemaakt.", + "filesystem.error.unknown": "Er is een onbekende fout opgetreden. Probeer het opnieuw.", + "filesystem.fetching": "Ophalen van map structuur...", + "filesystem.parent.directory": "Bovenliggende map", + "filter.all": "Alles", + "filter.status.title": "Filter op status", + "filter.status.downloading": "Downloaden", + "filter.status.seeding": "Uploaden", + "filter.status.completed": "Klaar", + "filter.status.active": "Actief", + "filter.status.inactive": "Inactief", + "filter.status.error": "Fout", + "filter.status.stopped": "Gestopt", + "filter.status.checking": "Controleren", + "filter.tracker.title": "Filter op tracker", + "filter.tag.title": "Filter op tag", + "filter.untagged": "Niet getagd", + "general.ago": "geleden", + "general.at": "bij", + "general.to": "naar", + "general.of": "van", + "general.clipboard.copy": "Kopiëren", + "general.clipboard.copied": "Gekoppeld", + "general.error.unknown": "Er is een onbekende fout opgetreden", + "mediainfo.execError": "Er is een fout opgetreden tijdens het uitvoeren van mediainfo op de server. Controleer of mediainformatie is geïnstalleerd en beschikbaar is in de PATH naar Flood.", + "mediainfo.fetching": "Ophalen...", + "mediainfo.heading": "Mediainfo Uitvoer", + "notification.feed.torrent.added.heading": "Feed item in wachtrij", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "Je hebt nog geen meldingen.", + "notification.torrent.finished.heading": "Downloaden voltooid", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Fout gerapporteerd", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Alles wissen", + "notification.showing": "Weergegeven", + "priority.dont.download": "Niet downloaden", + "priority.high": "Hoog", + "priority.low": "Laag", + "priority.normal": "Normaal", + "settings.bandwidth.slots.download.global.label": "Download slots globaal", + "settings.bandwidth.slots.download.label": "Download slots per torrent", + "settings.bandwidth.slots.heading": "Slots", + "settings.bandwidth.slots.upload.global.label": "Upload slots globaal", + "settings.bandwidth.slots.upload.label": "Upload slots per torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown downloadbeperking items", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown uploadbeperking items", + "settings.bandwidth.transferrate.global.throttle.download": "Globale downloadbeperking", + "settings.bandwidth.transferrate.global.throttle.upload": "Gobale uploadbeperking", + "settings.bandwidth.transferrate.heading": "Sneldheidsbeperkingen", + "settings.connectivity.dht.label": "Gebruik DHT", + "settings.connectivity.dht.port.label": "DHT Poort", + "settings.connectivity.dpd.heading": "Ongecentraliseerde Peer Ontdekking", + "settings.connectivity.incoming.heading": "Inkomende Connecties", + "settings.connectivity.ip.hostname.label": "Vermeld IP/Hostnaam", + "settings.connectivity.max.http.connections": "Maximaal aantal HTTP connecties", + "settings.connectivity.peer.exchange.label": "Gebruik Peer Exchange", + "settings.connectivity.peers.desired.label": "Gewenst aantal peers", + "settings.connectivity.peers.heading": "Leechers", + "settings.connectivity.peers.max.label": "Maximaal aantal peers", + "settings.connectivity.peers.min.label": "Minimaal aantal peers", + "settings.connectivity.peers.seeding.max.label": "Maximaal aantal peers seeding", + "settings.connectivity.peers.seeding.min.label": "Minimaal aantal peers seeding", + "settings.connectivity.port.open.label": "Open poort", + "settings.connectivity.port.randomize.label": "Willekeurige poort", + "settings.connectivity.port.range.label": "Poortbereik", + "settings.resources.disk.check.hash.label": "Controleer hash na downloaden", + "settings.resources.disk.download.location.label": "Standaard downloadlocatie", + "settings.resources.disk.heading": "Schijf", + "settings.resources.max.open.files": "Maximaal aantal open bestanden", + "settings.resources.memory.heading": "Geheugen", + "settings.resources.memory.max.label": "Maximaal geheugengebruik", + "settings.tabs.bandwidth": "Bandbreedte", + "settings.tabs.connectivity": "Connectiviteit", + "settings.tabs.heading": "Instellingen", + "settings.tabs.resources": "Hulpmiddelen", + "settings.tabs.authentication": "Authenticatie", + "settings.tabs.userinterface": "Gebruikersomgeving", + "settings.tabs.diskusage": "Schijf gebruik", + "settings.tabs.about": "Informatie", + "settings.ui.locale": "Landinstelling", + "settings.ui.language": "Taal", + "settings.ui.tag.selector.mode": "Voorkeur Label Selector", + "settings.ui.tag.selector.mode.single": "Enkelvoudige selectie", + "settings.ui.tag.selector.mode.multi": "Meerdvoudige selectie", + "settings.ui.torrent.list": "Torrent Lijst Weergave", + "settings.ui.torrent.size": "Torrentgrootte", + "settings.ui.torrent.size.expanded": "Uitgebreide weergave", + "settings.ui.torrent.size.condensed": "Gecondenseerde weergave", + "settings.ui.torrent.details.enabled": "Ingeschakeld", + "settings.ui.torrent.details.tags.placement": "In de uitgebreide weergave werken tags het beste aan het einde van de lijst.", + "settings.ui.torrent.context.menu.items.show": "Weergeven", + "settings.ui.displayed.details": "Torrent detail kolommen", + "settings.ui.displayed.context.menu.items": "Context menu-items", + "settings.diskusage.show": "Weergeven", + "settings.diskusage.mount.points": "Tekenpunten voor schijfgebruik", + "settings.about.flood": "Over Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Instellingen", + "sidebar.button.speedlimits": "Sneldheidsbeperkingen", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log uit", + "sidebar.search.placeholder": "Zoek torrents", + "sidebar.transferdata.downloaded": "Gedownload", + "sidebar.transferdata.uploaded": "Geupload", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOADEN", + "speed.unlimited": "Ongelimiteerd", + "unit.size.byte": "B", + "unit.size.kilobyte": "KB", + "unit.size.megabyte": "Mb", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "j", + "unit.time.week": "w", + "unit.time.day": "D", + "unit.time.hour": "h", + "unit.time.minute": "min", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Torrent toevoegen", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optionele cookie-naam=cookie-waarde", + "torrents.add.destination.label": "Downloadlocatie", + "torrents.add.destination.placeholder": "Downloadlocatie", + "torrents.add.heading": "Torrents toevoegen", + "torrents.add.start.label": "Torrent starten", + "torrents.add.tab.file.browse": "of klik om te bladeren.", + "torrents.add.tab.file.drop": "Sleep bestanden hierheen,", + "torrents.add.tab.file.title": "Via bestand", + "torrents.add.tab.url.input.placeholder": "Torrent URL", + "torrents.add.tab.url.title": "Via URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Aanmaken", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Labels", + "torrents.create.source.path.label": "Bron", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Basisnaam", + "torrents.create.base.name.input.placeholder": "Optioneel basisbestand of mapnaam van de torrent", + "torrents.create.comment.label": "Opmerking", + "torrents.create.comment.input.placeholder": "Optioneel commentaar in torrent bestand", + "torrents.create.info.source.label": "Info bron", + "torrents.create.info.source.input.placeholder": "Optionele bron item in infohash", + "torrents.create.is.private.label": "Privé", + "torrents.create.tags.input.placeholder": "Tags in Flood. Niet toegevoegd aan gemaakte torrent.", + "torrents.destination.base_path": "Gebruiken als basis pad", + "torrents.destination.completed": "Voltooid", + "torrents.details.actions.pause": "Pauzeer", + "torrents.details.actions.start": "Starten", + "torrents.details.actions.stop": "Stoppen", + "torrents.details.details": "Beschrijving", + "torrents.details.files": "Bestanden", + "torrents.details.files.loading": "Bestand details laden...", + "torrents.details.files.download.file": "{count, plural, =1 {Download bestand} other {Download bestanden}}", + "torrents.details.general.comment": "Opmerking", + "torrents.details.general.connected": "{connected} verbonden van {total}", + "torrents.details.general.date.added": "Toegevoegd op", + "torrents.details.general.date.created": "Aangemaakt op", + "torrents.details.general.downloaded": "Gedownload", + "torrents.details.general.free.disk.space": "Vrije schijfruimte", + "torrents.details.general.hash": "Toegangssleutel", + "torrents.details.general.heading.general": "Algemeen", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Overdracht", + "torrents.details.general.location": "Downloadlocatie", + "torrents.details.general.none": "Geen", + "torrents.details.general.peers": "Leechers", + "torrents.details.general.scheduler.ignored": "Genegeerd", + "torrents.details.general.scheduler.obeyed": "Uitgevoerd", + "torrents.details.general.scheduler": "Taakplanner", + "torrents.details.general.seeds": "Seeders", + "torrents.details.general.size": "Grootte", + "torrents.details.general.tags": "Labels", + "torrents.details.general.tracker.message": "Trackerbericht", + "torrents.details.general.type.private": "Privé", + "torrents.details.general.type.public": "Publiek", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Media Info", + "torrents.details.peers.no.data": "Er zijn geen peer-gegevens voor deze torrent.", + "torrents.details.peers": "Leechers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} bestand geselecteerd} other {{countElement} bestanden geselecteerd}}", + "torrents.details.selected.files.set.priority": "Wijzig prioriteit", + "torrents.details.trackers.no.data": "Er zijn geen tracker-gegevens voor deze torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Wis filters", + "torrents.list.context.check.hash": "Controleer hash", + "torrents.list.context.details": "Torrent details", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Downloadlocatie instellen", + "torrents.list.context.pause": "Pauzeer", + "torrents.list.context.download": "downloaden", + "torrents.list.context.priority": "Prioriteit", + "torrents.list.context.remove": "Verwijder", + "torrents.list.context.set.tags": "Tags instellen", + "torrents.list.context.set.trackers": "Trackers instellen", + "torrents.list.context.start": "Starten", + "torrents.list.context.stop": "Stoppen", + "torrents.list.no.torrents": "Geen torrents om te laten zien.", + "torrents.list.drop": "Sleep bestanden hierheen om ze toe te voegen.", + "torrents.list.cannot.connect": "Kan geen verbinding maken met de client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "van", + "torrents.move.button.set.location": "Wijzig downloadlocatie", + "torrents.move.button.state.setting": "Wijzigen...", + "torrents.move.data.label": "Verplaats bestanden", + "torrents.move.check_hash.label": "Controleer hash", + "torrents.move.heading": "Wijzig torrentlocatie", + "torrents.properties.date.added": "Datum toegevoegd", + "torrents.properties.comment": "Opmerking", + "torrents.properties.creation.date": "Aanmaak datum", + "torrents.properties.directory": "Downloadlocatie", + "torrents.properties.download.speed": "Downloadsnelheid", + "torrents.properties.download.total": "Gedownload", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Vrije schijfruimte", + "torrents.properties.hash": "Toegangssleutel", + "torrents.properties.ignore.schedule": "Negeer Taakplanner", + "torrents.properties.is.private": "Privé", + "torrents.properties.name": "Naam", + "torrents.properties.percentage": "Percentage compleet", + "torrents.properties.ratio": "Verhouding", + "torrents.properties.size": "Bestandsgrootte", + "torrents.properties.tags": "Labels", + "torrents.properties.tracker.message": "Tracker bericht", + "torrents.properties.upload.speed": "Uploadsnelheid", + "torrents.properties.upload.total": "Geupload", + "torrents.properties.seeds": "Seeders", + "torrents.properties.peers": "Leechers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Weet je zeker dat je {count, plural, =0 {geen torrents} =1 {één torrent} other {# torrents}} wilt verwijderen?", + "torrents.remove.delete.data": "Gegevens verwijderen", + "torrents.remove.error.no.torrents.selected": "Je hebt geen torrents geselecteerd.", + "torrents.remove": "Torrents verwijderen", + "torrents.set.tags.button.set": "Tags instellen", + "torrents.set.tags.heading": "Tags instellen", + "torrents.set.tags.enter.tags": "Tags invoeren", + "torrents.set.trackers.button.set": "Trackers instellen", + "torrents.set.trackers.heading": "Trackers instellen", + "torrents.set.trackers.enter.tracker": "Voer een tracker in", + "torrents.set.trackers.loading.trackers": "Nummers laden...", + "torrents.sort.title": "Sorteer op", + "connection-interruption.heading": "Kan geen verbinding maken met de client", + "status.diskusage.title": "Schijf gebruik", + "status.diskusage.used": "Gebruikt", + "status.diskusage.free": "Vrij", + "status.diskusage.total": "Totaal", + "locale.language.auto": "Automatisch", + "locale.language.translate": "Help mee met vertalen", + "dependency.loading.notifications": "Notificaties", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomie", + "dependency.loading.transfer.rate.details": "Gegevensoverdracht snelheid details", + "dependency.loading.transfer.history": "Gegevensoverdracht geschiedenis", + "dependency.loading.torrent.list": "Torrent lijst" +} diff --git a/client/src/javascript/i18n/translations/no.json b/client/src/javascript/i18n/translations/no.json new file mode 100644 index 000000000..eb8533efe --- /dev/null +++ b/client/src/javascript/i18n/translations/no.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start torrent", + "actionbar.button.stop.torrent": "Stopp torrent", + "actionbar.button.add.torrent": "Legg til torrent", + "actionbar.button.remove.torrent": "Fjern torrent", + "alert.torrent.add": "Helt lagt til {countElement} {count, plural, =1 {torrent} other {torrenter}}", + "alert.torrent.add.failed": "Kunne ikke legge til {countElement} {count, plural, =1 {torrent} other {torrenter}}", + "alert.torrent.move": "{countElement} {count, plural, =1 {torrent} other {torrenter}}", + "alert.torrent.move.failed": "Kunne ikke flytte {countElement} {count, plural, =1 {torrent} other {torrenter}}", + "alert.torrent.remove": "Fjernet {countElement} {count, plural, =1 {torrent} other {torrenter}}", + "alert.torrent.remove.failed": "Kunne ikke fjerne {countElement} {count, plural, =1 {torrent} other {torrenter}}", + "alert.settings.saved": "Innstillingene ble lagret.", + "auth.add.user": "Legg til bruker", + "auth.create.account": "Opprett konto", + "auth.create.an.account": "Opprett en konto", + "auth.create.an.account.intro": "Velkommen til flommet!", + "auth.current.user": "Gjeldende bruker", + "auth.error.username.empty": "Brukernavnfeltet kan ikke være tomt.", + "auth.error.password.empty": "Passord kan ikke være tomt.", + "auth.input.clear": "Clear", + "auth.log.in": "Logg inn", + "auth.login": "Innlogging", + "auth.login.intro": "Logg inn på din konto.", + "auth.password": "Passord", + "auth.user.accounts": "Bruker kontoer", + "auth.username": "Brukernavn", + "auth.admin": "Administrator", + "auth.message.not.admin": "Brukeren er ikke Admin", + "button.add": "Legg til", + "button.cancel": "Avbryt", + "button.close": "Close", + "button.download": "Nedlasting", + "button.no": "Nei", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Lagre innstillinger", + "button.save.feed": "Lagre", + "button.state.adding": "Legge til...", + "button.yes": "Ja", + "button.new": "Ny", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Tilkobling kunne ikke verifiseres.", + "connection.settings.client.select": "Klient", + "connection.settings.error.empty": "Tilkoblingsinnstillinger kan ikke være tomt.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Tilkobling type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Vert", + "connection.settings.rtorrent.host.input.placeholder": "Vertsnavn eller IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Sti", + "connection.settings.rtorrent.socket.input.placeholder": "Sti til stien", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "Nettadresse", + "connection.settings.qbittorrent.url.input.placeholder": "URL-adresse til qBittorrent Web API", + "connection.settings.qbittorrent.username": "Brukernavn", + "connection.settings.qbittorrent.username.input.placeholder": "Brukernavn", + "connection.settings.qbittorrent.password": "Passord", + "connection.settings.qbittorrent.password.input.placeholder": "Passord", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Tilkobling problem", + "connectivity.modal.content": "Kan ikke koble til klienten. Oppdater tilkoblingsinnstillingene.", + "feeds.add.automatic.download.rule": "Legg til regler for nedlasting", + "feeds.add.feed": "Legg til strøm", + "feeds.applicable.feed": "Gjeldende Feed", + "feeds.apply.tags": "Legg til etiketter", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Utelat Mønster", + "feeds.existing.feeds": "Eksisterende Feeder", + "feeds.existing.rules": "Eksisterende regler", + "feeds.interval": "Intervall", + "feeds.label": "Etikett", + "feeds.match.count": "{count, plural, =1 {# treff} other {# matcher}}", + "feeds.match.pattern": "Matchende mønster", + "feeds.match": "Kamp", + "feeds.exclude": "Ekskluder", + "feeds.no.feeds.available": "Ingen fôr tilgjengelig.", + "feeds.no.feeds.defined": "Ingen feeds definert.", + "feeds.no.items.matching": "Ingen elementer samsvarende med søkebegrep.", + "feeds.no.rules.defined": "Ingen regler definert.", + "feeds.regEx": "RegEks", + "feeds.select.feed": "Velg mating", + "feeds.select.interval": "Intervall", + "feeds.start.on.load": "Start ved lasting", + "feeds.tabs.download.rules": "Last ned regler", + "feeds.tabs.feeds": "Strøm", + "feeds.tabs.heading": "Torrent Feeder", + "feeds.tags": "Tagger", + "feeds.test.match": "Test samsvarsmønster", + "feeds.time.hr": "Timer", + "feeds.time.min": "Minutter", + "feeds.time.day": "Dager", + "feeds.torrent.destination": "Torrent destinasjon", + "feeds.url": "Nettadresse", + "feeds.search": "Søk etter uttrykk", + "feeds.search.term": "Søk etter uttrykk", + "feeds.validation.invalid.regular.expression": "Ugyldig regulært uttrykk.", + "feeds.validation.must.select.feed": "Du må velge en mating.", + "feeds.validation.must.specify.destination": "Du må spesifisere en destinasjon.", + "feeds.validation.must.specify.label": "Du må angi en etikett.", + "feeds.validation.must.specify.valid.feed.url": "Du må angi en gyldig feed URL.", + "feeds.validation.interval.not.positive": "Intervallet må være et positivt heltall.", + "feeds.browse.feeds": "Bla gjennom fôr", + "filesystem.empty.directory": "Tom mappe.", + "filesystem.error.eacces": "Flom har ikke tillatelse til å lese denne mappen.", + "filesystem.error.enoent": "Denne stien finnes ikke. Den vil bli opprettet.", + "filesystem.error.unknown": "Det oppstod en ukjent feil. Prøv på nytt.", + "filesystem.fetching": "Henter mappestruktur char@@0", + "filesystem.parent.directory": "Overordnet mappe", + "filter.all": "Alle", + "filter.status.title": "Filtrer etter status", + "filter.status.downloading": "Laster ned", + "filter.status.seeding": "Deler", + "filter.status.completed": "Complete", + "filter.status.active": "Aktiv", + "filter.status.inactive": "Inaktiv", + "filter.status.error": "Feil", + "filter.status.stopped": "Stoppet", + "filter.status.checking": "Kontrollerer", + "filter.tracker.title": "Filtrer etter tracker", + "filter.tag.title": "Filtrer etter tag", + "filter.untagged": "Umerket", + "general.ago": "siden", + "general.at": "på", + "general.to": "til", + "general.of": "av", + "general.clipboard.copy": "Kopier", + "general.clipboard.copied": "Kopiert", + "general.error.unknown": "En ukjent feil har oppstått", + "mediainfo.execError": "Det oppstod en feil under kjøring av mediainfo på serveren. Sjekk at mediainfo er installert og tilgjengelig i PATH flom.", + "mediainfo.fetching": "Henter...", + "mediainfo.heading": "Mediainfo utdata", + "notification.feed.torrent.added.heading": "Strøm enhet i kø", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Nedlasting ferdig", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Feil rapportert", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Fjern alle", + "notification.showing": "Viser", + "priority.dont.download": "Ikke last ned", + "priority.high": "Høy", + "priority.low": "Lav", + "priority.normal": "Vanlig", + "settings.bandwidth.slots.download.global.label": "Last ned slots Global", + "settings.bandwidth.slots.download.label": "Last ned slot per Torrent", + "settings.bandwidth.slots.heading": "Spor tilgjengelighet", + "settings.bandwidth.slots.upload.global.label": "Last opp plasser globalt", + "settings.bandwidth.slots.upload.label": "Last opp plasser per torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Forhåndsinnstillinger for rulleliste: Last ned", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Forhåndsinnstillinger for rulleliste: Last opp", + "settings.bandwidth.transferrate.global.throttle.download": "Global nedlastingshastighet Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Global opplastingshastighet Throttle", + "settings.bandwidth.transferrate.heading": "Overfør bokstaver", + "settings.connectivity.dht.label": "Aktiver DHT", + "settings.connectivity.dht.port.label": "DHT port", + "settings.connectivity.dpd.heading": "Desentralisert Peer Oppdagelse", + "settings.connectivity.incoming.heading": "Innkommende tilkoblinger", + "settings.connectivity.ip.hostname.label": "Rapportert IP/Vertsnavn", + "settings.connectivity.max.http.connections": "Maksimal HTTP-tilkobling (Automatic Translation)", + "settings.connectivity.peer.exchange.label": "Aktiver Peer Exchange", + "settings.connectivity.peers.desired.label": "Mottakere utsendt", + "settings.connectivity.peers.heading": "Likemenn", + "settings.connectivity.peers.max.label": "Maksimalt antall kilder", + "settings.connectivity.peers.min.label": "Minimum klienter", + "settings.connectivity.peers.seeding.max.label": "Maks spredning til klienter", + "settings.connectivity.peers.seeding.min.label": "Minimum kilder for å dele", + "settings.connectivity.port.open.label": "Åpne port", + "settings.connectivity.port.randomize.label": "Tilfeldig port", + "settings.connectivity.port.range.label": "Port Rekkefølge", + "settings.resources.disk.check.hash.label": "Verifiser Hash ved ferdigstillelse", + "settings.resources.disk.download.location.label": "Standard nedlastingsmappe", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maksimalt antall åpne filer", + "settings.resources.memory.heading": "Minne", + "settings.resources.memory.max.label": "Maks minnebruk", + "settings.tabs.bandwidth": "Båndbredde", + "settings.tabs.connectivity": "Tilkobling", + "settings.tabs.heading": "Innstillinger", + "settings.tabs.resources": "Ressurser", + "settings.tabs.authentication": "Autentisering", + "settings.tabs.userinterface": "Brukers grensesnitt", + "settings.tabs.diskusage": "Bruk av diskplass", + "settings.tabs.about": "Om", + "settings.ui.locale": "Språk", + "settings.ui.language": "Språk", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Vis torrent liste", + "settings.ui.torrent.size": "Torrent størrelse", + "settings.ui.torrent.size.expanded": "Utvidet visning", + "settings.ui.torrent.size.condensed": "Kondensert visning", + "settings.ui.torrent.details.enabled": "Aktivert", + "settings.ui.torrent.details.tags.placement": "I den utvidede visningen, fungerer merkelapper best på slutten av listen.", + "settings.ui.torrent.context.menu.items.show": "Vis", + "settings.ui.displayed.details": "Torrent Detalj kolonner", + "settings.ui.displayed.context.menu.items": "Sammenhengende menyelementer", + "settings.diskusage.show": "Vis", + "settings.diskusage.mount.points": "Bruk diskens ridningspoeng", + "settings.about.flood": "Om Flood", + "sidebar.button.feeds": "Strøm", + "sidebar.button.settings": "Innstillinger", + "sidebar.button.speedlimits": "Grenseverdier for hastighet", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Logg ut", + "sidebar.search.placeholder": "Søk torrents", + "sidebar.transferdata.downloaded": "Nedlastet", + "sidebar.transferdata.uploaded": "Opplastet", + "sidebar.speedlimits.download": "LAST NED", + "sidebar.speedlimits.upload": "LAST OPP", + "speed.unlimited": "Ubegrenset", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "Mb", + "unit.size.gigabyte": "NO", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "uke", + "unit.time.day": "D", + "unit.time.hour": "t", + "unit.time.minute": "min", + "unit.time.second": "S", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Legg til torrent", + "torrents.add.cookies.label": "Informasjonskapsler", + "torrents.add.cookies.input.placeholder": "Valgfri cookie-name=cookie-value", + "torrents.add.destination.label": "Mål", + "torrents.add.destination.placeholder": "Mål", + "torrents.add.heading": "Legge til torrenter", + "torrents.add.start.label": "Start torrent", + "torrents.add.tab.file.browse": "eller klikk for å bla gjennom", + "torrents.add.tab.file.drop": "Slipp noen filer her,", + "torrents.add.tab.file.title": "Etter fil", + "torrents.add.tab.url.input.placeholder": "Torrent URL eller Magnet Link", + "torrents.add.tab.url.title": "Etter URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Opprett", + "torrents.add.torrents.label": "Torrenter", + "torrents.add.tags": "Tagger", + "torrents.create.source.path.label": "Kilde", + "torrents.create.trackers.label": "Sporere", + "torrents.create.tracker.input.placeholder": "Sporingsagent URL", + "torrents.create.base.name.label": "Navn på database", + "torrents.create.base.name.input.placeholder": "Valgfri basisfil eller mappenavn på torrenten", + "torrents.create.comment.label": "Kommentar", + "torrents.create.comment.input.placeholder": "Valgfri kommentar i torrentfil", + "torrents.create.info.source.label": "Info kilde", + "torrents.create.info.source.input.placeholder": "Valgfri kildeoppføring i infohash", + "torrents.create.is.private.label": "Privat", + "torrents.create.tags.input.placeholder": "Etiketter i flommen. Ikke lagt til i opprettet torrent.", + "torrents.destination.base_path": "Bruk som basis sti", + "torrents.destination.completed": "Fullført", + "torrents.details.actions.pause": "pause", + "torrents.details.actions.start": "Begynn", + "torrents.details.actions.stop": "Stopp", + "torrents.details.details": "Detaljer", + "torrents.details.files": "Filer", + "torrents.details.files.loading": "Laster fildetaljer...", + "torrents.details.files.download.file": "{count, plural, =1 {Last ned filen} other {Last ned filer}}", + "torrents.details.general.comment": "Kommentar", + "torrents.details.general.connected": "{connected} koblet til {total}", + "torrents.details.general.date.added": "Lagt", + "torrents.details.general.date.created": "Opprettet dato", + "torrents.details.general.downloaded": "Nedlastet", + "torrents.details.general.free.disk.space": "Ledig diskplass", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Generelt", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Sporingsagent", + "torrents.details.general.heading.transfer": "Overfør", + "torrents.details.general.location": "Sted", + "torrents.details.general.none": "Ingen", + "torrents.details.general.peers": "Likemenn", + "torrents.details.general.scheduler.ignored": "Ignorert", + "torrents.details.general.scheduler.obeyed": "Uhørt", + "torrents.details.general.scheduler": "Oppgaveplanlegging", + "torrents.details.general.seeds": "Frø", + "torrents.details.general.size": "Størrelse", + "torrents.details.general.tags": "Tagger", + "torrents.details.general.tracker.message": "Melding om sporingsagent", + "torrents.details.general.type.private": "Privat", + "torrents.details.general.type.public": "Offentlig", + "torrents.details.general.type": "Type:", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Det er ingen kilder data for denne torren.", + "torrents.details.peers": "Likemenn", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} valgt fil} other {{countElement} valgte filer}}", + "torrents.details.selected.files.set.priority": "Angi prioritet", + "torrents.details.trackers.no.data": "Det finnes ingen data for tracker for denne torren.", + "torrents.details.trackers.type": "Type:", + "torrents.details.trackers": "Sporere", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Fjern filtre", + "torrents.list.context.check.hash": "Sjekk Hash", + "torrents.list.context.details": "Torrent detaljer", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Angi torrent plassering", + "torrents.list.context.pause": "pause", + "torrents.list.context.download": "Nedlasting", + "torrents.list.context.priority": "Prioritet", + "torrents.list.context.remove": "Fjern", + "torrents.list.context.set.tags": "Angi Etiketter", + "torrents.list.context.set.trackers": "Angi trackere", + "torrents.list.context.start": "Begynn", + "torrents.list.context.stop": "Stopp", + "torrents.list.no.torrents": "Ingen torrenter å vise.", + "torrents.list.drop": "Slipp filer her for å legge dem til.", + "torrents.list.cannot.connect": "Kan ikke koble til annonsøren.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "av", + "torrents.move.button.set.location": "Angi sted", + "torrents.move.button.state.setting": "Innstillinger...", + "torrents.move.data.label": "Flytt data", + "torrents.move.check_hash.label": "Sjekk hash", + "torrents.move.heading": "Angi torrent plassering", + "torrents.properties.date.added": "Lagt", + "torrents.properties.comment": "Kommentar", + "torrents.properties.creation.date": "Opprettet dato", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Nedlastings hastighet", + "torrents.properties.download.total": "Nedlastet", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Ledig diskplass", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignorer planlegger", + "torrents.properties.is.private": "Privat", + "torrents.properties.name": "Navn", + "torrents.properties.percentage": "Prosent fullført", + "torrents.properties.ratio": "Forhold", + "torrents.properties.size": "Fil Størrelse", + "torrents.properties.tags": "Tagger", + "torrents.properties.tracker.message": "Melding om sporingsagent", + "torrents.properties.upload.speed": "Opplastnings hastighet", + "torrents.properties.upload.total": "Opplastet", + "torrents.properties.seeds": "Frø", + "torrents.properties.peers": "Likemenn", + "torrents.properties.trackers": "Sporere", + "torrents.remove.are.you.sure": "Er du sikker på at du vil fjerne {count, plural, =1 {# torrent} other {# torrenter}}?", + "torrents.remove.delete.data": "Slett data", + "torrents.remove.error.no.torrents.selected": "Du har ikke valgt noen torrenter.", + "torrents.remove": "Fjern torrenter", + "torrents.set.tags.button.set": "Angi Etiketter", + "torrents.set.tags.heading": "Angi Etiketter", + "torrents.set.tags.enter.tags": "Angi tagger", + "torrents.set.trackers.button.set": "Angi trackere", + "torrents.set.trackers.heading": "Angi trackere", + "torrents.set.trackers.enter.tracker": "Angi en tracker", + "torrents.set.trackers.loading.trackers": "Laster sporere...", + "torrents.sort.title": "Sorter etter", + "connection-interruption.heading": "Kan ikke koble til klienten", + "status.diskusage.title": "Bruk av diskplass", + "status.diskusage.used": "Brukt", + "status.diskusage.free": "Gratis", + "status.diskusage.total": "Totalt", + "locale.language.auto": "Automatisk", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Varsler", + "dependency.loading.torrent.taxonomy": "Torrent taksonomi", + "dependency.loading.transfer.rate.details": "Detaljer for overføring av data", + "dependency.loading.transfer.history": "Data overførings historikk", + "dependency.loading.torrent.list": "Liste over torrent" +} diff --git a/client/src/javascript/i18n/translations/pl.json b/client/src/javascript/i18n/translations/pl.json new file mode 100644 index 000000000..92c53894e --- /dev/null +++ b/client/src/javascript/i18n/translations/pl.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Uruchom torrent", + "actionbar.button.stop.torrent": "Zatrzymaj torrent", + "actionbar.button.add.torrent": "Dodaj torrent", + "actionbar.button.remove.torrent": "Usuń torrent", + "alert.torrent.add": "Pomyślnie dodano {countElement} {count, plural, =1 {torrent} other {torrenty}}.", + "alert.torrent.add.failed": "Nie udało się dodać {countElement} {count, plural, =1 {torrent} other {torrenty}}.", + "alert.torrent.move": "Pomyślnie przeniesiono {countElement} {count, plural, =1 {torrenty} other {torrenty}}.", + "alert.torrent.move.failed": "Nie udało się przenieść {countElement} {count, plural, =1 {torrentów} other {torrentów}}.", + "alert.torrent.remove": "Pomyślnie usunięto {countElement} {count, plural, =1 {torrenty} other {torrenty}}.", + "alert.torrent.remove.failed": "Nie udało się usunąć {countElement} {count, plural, =1 {torrentów} other {torrentów}}.", + "alert.settings.saved": "Zapisano ustawienia.", + "auth.add.user": "Dodaj użytkownika", + "auth.create.account": "Utwórz konto", + "auth.create.an.account": "Utwórz konto", + "auth.create.an.account.intro": "Witaj w powodzie!", + "auth.current.user": "Bieżący użytkownik", + "auth.error.username.empty": "Nazwa użytkownika nie może być pusta.", + "auth.error.password.empty": "Hasło nie może być puste.", + "auth.input.clear": "Clear", + "auth.log.in": "Zaloguj się", + "auth.login": "Logowanie", + "auth.login.intro": "Zaloguj się do swojego konta.", + "auth.password": "Hasło", + "auth.user.accounts": "Konta użytkowników", + "auth.username": "Nazwa użytkownika", + "auth.admin": "Administrator", + "auth.message.not.admin": "Użytkownik nie jest administratorem", + "button.add": "Dodaj", + "button.cancel": "Anuluj", + "button.close": "Close", + "button.download": "Pobierz", + "button.no": "Nie", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Zapisz ustawienia", + "button.save.feed": "Zapisz", + "button.state.adding": "Dodawanie...", + "button.yes": "Tak", + "button.new": "Nowy", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Nie można zweryfikować połączenia.", + "connection.settings.client.select": "Klient", + "connection.settings.error.empty": "Ustawienia połączenia nie mogą być puste.", + "connection.settings.rtorrent": "Rtorrent", + "connection.settings.rtorrent.type": "Typ połączenia", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Nazwa hosta lub IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Ścieżka", + "connection.settings.rtorrent.socket.input.placeholder": "Ścieżka do gniazda", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "Adres URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL do qBittorrent Web API", + "connection.settings.qbittorrent.username": "Nazwa użytkownika", + "connection.settings.qbittorrent.username.input.placeholder": "Nazwa użytkownika", + "connection.settings.qbittorrent.password": "Hasło", + "connection.settings.qbittorrent.password.input.placeholder": "Hasło", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Problem z połączeniem", + "connectivity.modal.content": "Nie można połączyć się z klientem. Proszę zaktualizować ustawienia połączenia.", + "feeds.add.automatic.download.rule": "Dodaj regułę pobierania", + "feeds.add.feed": "Dodaj kanał", + "feeds.applicable.feed": "Stosowany kanał", + "feeds.apply.tags": "Zastosuj tagi", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Wyklucz wzór", + "feeds.existing.feeds": "Istniejące kanały", + "feeds.existing.rules": "Istniejące reguły", + "feeds.interval": "Interwał", + "feeds.label": "Etykieta", + "feeds.match.count": "{count, plural, =1 {# pasuje do} other {# pasuje do}}", + "feeds.match.pattern": "Wzorzec dopasowania", + "feeds.match": "Dopasowanie", + "feeds.exclude": "Wyklucz", + "feeds.no.feeds.available": "Brak dostępnych kanałów.", + "feeds.no.feeds.defined": "Nie zdefiniowano kanałów.", + "feeds.no.items.matching": "Brak elementów pasujących do wyszukiwarki.", + "feeds.no.rules.defined": "Nie zdefiniowano reguł.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Wybierz kanał", + "feeds.select.interval": "Interwał", + "feeds.start.on.load": "Zacznij od załadowania", + "feeds.tabs.download.rules": "Zasady pobierania", + "feeds.tabs.feeds": "Kanały", + "feeds.tabs.heading": "Kanały torrent", + "feeds.tags": "Tagi", + "feeds.test.match": "Testowy wzór dopasowania", + "feeds.time.hr": "Godziny", + "feeds.time.min": "Protokoły", + "feeds.time.day": "Dni", + "feeds.torrent.destination": "Przeznaczenie torrentów", + "feeds.url": "Adres URL", + "feeds.search": "Szukaj frazy", + "feeds.search.term": "Szukaj frazy", + "feeds.validation.invalid.regular.expression": "Nieprawidłowe wyrażenie regularne.", + "feeds.validation.must.select.feed": "Musisz wybrać kanał.", + "feeds.validation.must.specify.destination": "Musisz określić miejsce docelowe.", + "feeds.validation.must.specify.label": "Musisz określić etykietę.", + "feeds.validation.must.specify.valid.feed.url": "Musisz podać poprawny adres URL kanału.", + "feeds.validation.interval.not.positive": "Interwał musi być dodatnią liczbą całkowitą.", + "feeds.browse.feeds": "Przeglądaj kanały", + "filesystem.empty.directory": "Pusty katalog.", + "filesystem.error.eacces": "Powódź nie ma uprawnień do odczytu tego katalogu.", + "filesystem.error.enoent": "Ta ścieżka nie istnieje. Zostanie utworzona.", + "filesystem.error.unknown": "Wystąpił nieznany błąd. Spróbuj ponownie.", + "filesystem.fetching": "Pobieranie struktury katalogu...", + "filesystem.parent.directory": "Katalog nadrzędny", + "filter.all": "Wszystkie", + "filter.status.title": "Filtruj według statusu", + "filter.status.downloading": "Pobieranie", + "filter.status.seeding": "Nasiona", + "filter.status.completed": "Complete", + "filter.status.active": "Aktywne", + "filter.status.inactive": "Nieaktywny", + "filter.status.error": "Błąd", + "filter.status.stopped": "Zatrzymano", + "filter.status.checking": "Sprawdzanie", + "filter.tracker.title": "Filtruj według trackera", + "filter.tag.title": "Filtruj według tagu", + "filter.untagged": "Nietagowane", + "general.ago": "temu", + "general.at": "w", + "general.to": "do", + "general.of": "z", + "general.clipboard.copy": "Kopiuj", + "general.clipboard.copied": "Skopiowano", + "general.error.unknown": "Wystąpił nieznany błąd", + "mediainfo.execError": "Wystąpił błąd podczas uruchamiania mediainfo na serwerze. Sprawdź, czy mediainfo jest zainstalowany i dostępny w PATH do powodzi.", + "mediainfo.fetching": "Pobieranie...", + "mediainfo.heading": "Wyjście Mediainfo", + "notification.feed.torrent.added.heading": "Element RSS w kolejce", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Pobieranie zakończone", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Błąd zgłoszony", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Wyczyść wszystko", + "notification.showing": "Wyświetlanie", + "priority.dont.download": "Nie pobieraj", + "priority.high": "Wysoka", + "priority.low": "Niski", + "priority.normal": "Normalny", + "settings.bandwidth.slots.download.global.label": "Pobierz gniazda globalne", + "settings.bandwidth.slots.download.label": "Pobierz miejsca na torrent", + "settings.bandwidth.slots.heading": "Dostępność miejsca", + "settings.bandwidth.slots.upload.global.label": "Prześlij Sloty globalne", + "settings.bandwidth.slots.upload.label": "Prześlij Sloty na torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Rozwijane ustawienia: Pobierz", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Rozwijane ustawienia: Załaduj", + "settings.bandwidth.transferrate.global.throttle.download": "Globalna przepustnica szybkości pobierania", + "settings.bandwidth.transferrate.global.throttle.upload": "Globalna przepustnica Szybkości Wysyłania", + "settings.bandwidth.transferrate.heading": "Prędkość transferu przepustnicy", + "settings.connectivity.dht.label": "Włącz DHT", + "settings.connectivity.dht.port.label": "Port DHT", + "settings.connectivity.dpd.heading": "Odkrycie zdecentralizowanego peera", + "settings.connectivity.incoming.heading": "Połączenia przychodzące", + "settings.connectivity.ip.hostname.label": "Zgłoszone IP/nazwa hosta", + "settings.connectivity.max.http.connections": "Maksymalna liczba połączeń HTTP", + "settings.connectivity.peer.exchange.label": "Włącz wymianę peer", + "settings.connectivity.peers.desired.label": "Pożądane rówieśniki", + "settings.connectivity.peers.heading": "Uczestnicy", + "settings.connectivity.peers.max.label": "Maksymalna liczba peerów", + "settings.connectivity.peers.min.label": "Minimalna liczba peerów", + "settings.connectivity.peers.seeding.max.label": "Maksymalna ilość seedów peerów", + "settings.connectivity.peers.seeding.min.label": "Minimalne seedowanie peerów", + "settings.connectivity.port.open.label": "Otwórz port", + "settings.connectivity.port.randomize.label": "Losowy port", + "settings.connectivity.port.range.label": "Zakres portu", + "settings.resources.disk.check.hash.label": "Zweryfikuj skrót po zakończeniu", + "settings.resources.disk.download.location.label": "Domyślny katalog pobierania", + "settings.resources.disk.heading": "Dysk", + "settings.resources.max.open.files": "Maksymalna liczba otwartych plików", + "settings.resources.memory.heading": "Pamięć", + "settings.resources.memory.max.label": "Maksymalne zużycie pamięci", + "settings.tabs.bandwidth": "Przepustowość", + "settings.tabs.connectivity": "Łączność", + "settings.tabs.heading": "Ustawienia", + "settings.tabs.resources": "Zasoby", + "settings.tabs.authentication": "Uwierzytelnianie", + "settings.tabs.userinterface": "Interfejs użytkownika", + "settings.tabs.diskusage": "Użycie dysku", + "settings.tabs.about": "O programie", + "settings.ui.locale": "Język", + "settings.ui.language": "Język", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Wyświetlanie listy torrentów", + "settings.ui.torrent.size": "Rozmiar torrenta", + "settings.ui.torrent.size.expanded": "Widok rozszerzony", + "settings.ui.torrent.size.condensed": "Skondensowany widok", + "settings.ui.torrent.details.enabled": "Włączone", + "settings.ui.torrent.details.tags.placement": "W rozszerzonym widoku tagi działają najlepiej na końcu listy.", + "settings.ui.torrent.context.menu.items.show": "Pokaż", + "settings.ui.displayed.details": "Kolumny szczegółów torrenta", + "settings.ui.displayed.context.menu.items": "Pozycje menu kontekstowego", + "settings.diskusage.show": "Pokaż", + "settings.diskusage.mount.points": "Punkty montowania dysku", + "settings.about.flood": "O powodzie", + "sidebar.button.feeds": "Kanały", + "sidebar.button.settings": "Ustawienia", + "sidebar.button.speedlimits": "Ograniczenia prędkości", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Wyloguj", + "sidebar.search.placeholder": "Szukaj torrentów", + "sidebar.transferdata.downloaded": "Pobrano", + "sidebar.transferdata.uploaded": "Przesłano", + "sidebar.speedlimits.download": "POBIERZ", + "sidebar.speedlimits.upload": "PRZEŚLIJ", + "speed.unlimited": "Nieograniczona", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "tyg.", + "unit.time.day": "ob", + "unit.time.hour": "godz.", + "unit.time.minute": "m", + "unit.time.second": "u rot ob", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Dodaj torrent", + "torrents.add.cookies.label": "Pliki cookie", + "torrents.add.cookies.input.placeholder": "Opcjonalnie nazwa ciasteczka=wartość ciasteczka", + "torrents.add.destination.label": "Miejsce przeznaczenia", + "torrents.add.destination.placeholder": "Miejsce przeznaczenia", + "torrents.add.heading": "Dodaj torrenty", + "torrents.add.start.label": "Uruchom torrent", + "torrents.add.tab.file.browse": "lub kliknij, aby przeglądać", + "torrents.add.tab.file.drop": "Upuść pliki tutaj,", + "torrents.add.tab.file.title": "Wg pliku", + "torrents.add.tab.url.input.placeholder": "URL torrenta lub Magnet Link", + "torrents.add.tab.url.title": "Według adresu URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Utwórz", + "torrents.add.torrents.label": "Torrenty", + "torrents.add.tags": "Tagi", + "torrents.create.source.path.label": "Źródło", + "torrents.create.trackers.label": "Śledzenie", + "torrents.create.tracker.input.placeholder": "URL trackera", + "torrents.create.base.name.label": "Nazwa podstawowa", + "torrents.create.base.name.input.placeholder": "Opcjonalny plik podstawowy lub nazwa katalogu torrenta", + "torrents.create.comment.label": "Komentarz", + "torrents.create.comment.input.placeholder": "Opcjonalny komentarz w pliku torrent", + "torrents.create.info.source.label": "Źródło informacji", + "torrents.create.info.source.input.placeholder": "Opcjonalny wpis źródłowy w infohash", + "torrents.create.is.private.label": "Prywatny", + "torrents.create.tags.input.placeholder": "Tagi w powodzie. Nie dodano do utworzonego torrentu.", + "torrents.destination.base_path": "Użyj jako ścieżki bazowej", + "torrents.destination.completed": "Zakończone", + "torrents.details.actions.pause": "Wstrzymaj", + "torrents.details.actions.start": "Rozpocznij", + "torrents.details.actions.stop": "Zatrzymaj", + "torrents.details.details": "Szczegóły", + "torrents.details.files": "Pliki", + "torrents.details.files.loading": "Ładowanie szczegółów pliku...", + "torrents.details.files.download.file": "{count, plural, =1 {Pobierz plik} other {Pobierz pliki}}", + "torrents.details.general.comment": "Komentarz", + "torrents.details.general.connected": "{connected} połączony z {total}", + "torrents.details.general.date.added": "Dodano", + "torrents.details.general.date.created": "Data utworzenia", + "torrents.details.general.downloaded": "Pobrano", + "torrents.details.general.free.disk.space": "Wolne miejsce na dysku", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Ogólny", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Śledzenie", + "torrents.details.general.heading.transfer": "Przelew", + "torrents.details.general.location": "Lokalizacja", + "torrents.details.general.none": "Brak", + "torrents.details.general.peers": "Uczestnicy", + "torrents.details.general.scheduler.ignored": "Ignorowane", + "torrents.details.general.scheduler.obeyed": "obfite", + "torrents.details.general.scheduler": "Harmonogram", + "torrents.details.general.seeds": "Nasiona", + "torrents.details.general.size": "Rozmiar", + "torrents.details.general.tags": "Tagi", + "torrents.details.general.tracker.message": "Wiadomość trackera", + "torrents.details.general.type.private": "Prywatny", + "torrents.details.general.type.public": "Publiczne", + "torrents.details.general.type": "Typ", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Brak danych peerów dla tego torrentu.", + "torrents.details.peers": "Uczestnicy", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} wybrany plik} other {{countElement} wybrano pliki}}", + "torrents.details.selected.files.set.priority": "Ustaw priorytet", + "torrents.details.trackers.no.data": "Brak danych trackera dla tego torrentu.", + "torrents.details.trackers.type": "Typ", + "torrents.details.trackers": "Śledzenie", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Wyczyść filtry", + "torrents.list.context.check.hash": "Sprawdź hash", + "torrents.list.context.details": "Szczegóły torrenta", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Ustaw lokalizację torrenta", + "torrents.list.context.pause": "Wstrzymaj", + "torrents.list.context.download": "Pobierz", + "torrents.list.context.priority": "Priorytet", + "torrents.list.context.remove": "Usuń", + "torrents.list.context.set.tags": "Ustaw tagi", + "torrents.list.context.set.trackers": "Ustaw trackery", + "torrents.list.context.start": "Rozpocznij", + "torrents.list.context.stop": "Zatrzymaj", + "torrents.list.no.torrents": "Brak torrentów do wyświetlenia.", + "torrents.list.drop": "Upuść pliki tutaj, aby je dodać.", + "torrents.list.cannot.connect": "Nie można połączyć się z klientem.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "z", + "torrents.move.button.set.location": "Ustaw lokalizację", + "torrents.move.button.state.setting": "Ustawienie...", + "torrents.move.data.label": "Przenieś dane", + "torrents.move.check_hash.label": "Sprawdź skrót", + "torrents.move.heading": "Ustaw lokalizację torrenta", + "torrents.properties.date.added": "Dodano", + "torrents.properties.comment": "Komentarz", + "torrents.properties.creation.date": "Data utworzenia", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Prędkość pobierania", + "torrents.properties.download.total": "Pobrano", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Wolne miejsce na dysku", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignoruj harmonogram", + "torrents.properties.is.private": "Prywatny", + "torrents.properties.name": "Nazwisko", + "torrents.properties.percentage": "Procent zakończony", + "torrents.properties.ratio": "Stosunek", + "torrents.properties.size": "Rozmiar pliku", + "torrents.properties.tags": "Tagi", + "torrents.properties.tracker.message": "Wiadomość trackera", + "torrents.properties.upload.speed": "Prędkość przesyłania", + "torrents.properties.upload.total": "Przesłano", + "torrents.properties.seeds": "Nasiona", + "torrents.properties.peers": "Uczestnicy", + "torrents.properties.trackers": "Śledzenie", + "torrents.remove.are.you.sure": "Czy na pewno chcesz usunąć {count, plural, =1 {# torrenty} other {# torrentów}}?", + "torrents.remove.delete.data": "Usuń dane", + "torrents.remove.error.no.torrents.selected": "Nie wybrałeś żadnych torrentów.", + "torrents.remove": "Usuń torrenty", + "torrents.set.tags.button.set": "Ustaw tagi", + "torrents.set.tags.heading": "Ustaw tagi", + "torrents.set.tags.enter.tags": "Wprowadź tagi", + "torrents.set.trackers.button.set": "Ustaw trackery", + "torrents.set.trackers.heading": "Ustaw trackery", + "torrents.set.trackers.enter.tracker": "Wprowadź tracker", + "torrents.set.trackers.loading.trackers": "Ładowanie trackerów...", + "torrents.sort.title": "Sortuj wg", + "connection-interruption.heading": "Nie można połączyć się z klientem", + "status.diskusage.title": "Użycie dysku", + "status.diskusage.used": "Używane", + "status.diskusage.free": "Darmowe", + "status.diskusage.total": "Łącznie", + "locale.language.auto": "Automatyczne", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Powiadomienia", + "dependency.loading.torrent.taxonomy": "Taksonomia torrent", + "dependency.loading.transfer.rate.details": "Szczegóły tempa transferu danych", + "dependency.loading.transfer.history": "Historia transferu danych", + "dependency.loading.torrent.list": "Lista torrentów" +} diff --git a/client/src/javascript/i18n/translations/pt.json b/client/src/javascript/i18n/translations/pt.json new file mode 100644 index 000000000..cf83a6bc8 --- /dev/null +++ b/client/src/javascript/i18n/translations/pt.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Iniciar Torrent", + "actionbar.button.stop.torrent": "Parar Torrent", + "actionbar.button.add.torrent": "Adicionar Torrent", + "actionbar.button.remove.torrent": "Remover Torrent", + "alert.torrent.add": "Adicionado com sucesso {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Falha ao adicionar {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Movido com sucesso {countElement} {count, plural, =1 {torrent} other {torrents}}", + "alert.torrent.move.failed": "Falha ao mover {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Removido com sucesso {countElement} {count, plural, =1 {torrent removido com sucesso} other {torrents}}.", + "alert.torrent.remove.failed": "Falha ao remover {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Configurações guardas com sucesso.", + "auth.add.user": "Adicionar Utilizador", + "auth.create.account": "Criar conta", + "auth.create.an.account": "Criar uma conta", + "auth.create.an.account.intro": "Bem-vindo ao Flood!", + "auth.current.user": "Utilizador atual", + "auth.error.username.empty": "Nome de utilizador não pode estar vazio.", + "auth.error.password.empty": "A senha não pode estar vazia.", + "auth.input.clear": "Clear", + "auth.log.in": "Iniciar sessão", + "auth.login": "Iniciar sessão", + "auth.login.intro": "Inicie sessão na sua conta.", + "auth.password": "Palavra-passe", + "auth.user.accounts": "Contas dos utilizadores", + "auth.username": "Nome de Utilizador", + "auth.admin": "Administrador", + "auth.message.not.admin": "Utilizador não é Administrador", + "button.add": "Adicionar", + "button.cancel": "Cancelar", + "button.close": "Close", + "button.download": "Transferir", + "button.no": "Não", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Guardar Configurações", + "button.save.feed": "Guardar", + "button.state.adding": "Adicionando...", + "button.yes": "Sim", + "button.new": "Novidades", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "A conexão não pôde ser verificada.", + "connection.settings.client.select": "Cliente", + "connection.settings.error.empty": "Configurações de conexão não podem estar vazias.", + "connection.settings.rtorrent": "Torrent", + "connection.settings.rtorrent.type": "Tipo de conexão", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Servidor", + "connection.settings.rtorrent.host.input.placeholder": "Nome do host ou IP", + "connection.settings.rtorrent.port": "Porta", + "connection.settings.rtorrent.port.input.placeholder": "Porta", + "connection.settings.rtorrent.socket": "Caminho", + "connection.settings.rtorrent.socket.input.placeholder": "Caminho para o socket", + "connection.settings.qbittorrent": "Bittorrent", + "connection.settings.qbittorrent.url": "URL:", + "connection.settings.qbittorrent.url.input.placeholder": "URL para qBittorrent Web API", + "connection.settings.qbittorrent.username": "Usuário:", + "connection.settings.qbittorrent.username.input.placeholder": "Usuário:", + "connection.settings.qbittorrent.password": "Palavra-passe", + "connection.settings.qbittorrent.password.input.placeholder": "Palavra-passe", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Problema de conectividade", + "connectivity.modal.content": "Não é possível conectar-se ao cliente. Por favor atualize as configurações de conexão.", + "feeds.add.automatic.download.rule": "Adicionar regra de download", + "feeds.add.feed": "Adicionar Feed", + "feeds.applicable.feed": "Feed aplicável", + "feeds.apply.tags": "Aplicar Etiquetas", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Excluir padrão", + "feeds.existing.feeds": "Feeds existentes", + "feeds.existing.rules": "Regras existentes", + "feeds.interval": "Intervalo", + "feeds.label": "Etiqueta", + "feeds.match.count": "{count, plural, =1 {# match} other {# match}}", + "feeds.match.pattern": "Padrão Correspondente", + "feeds.match": "Corresponder", + "feeds.exclude": "Excluir", + "feeds.no.feeds.available": "Não há feeds disponíveis.", + "feeds.no.feeds.defined": "Não foi definido nenhum feed.", + "feeds.no.items.matching": "Nenhum item corresponde ao termo de pesquisa.", + "feeds.no.rules.defined": "Nenhuma regra definida.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Selecionar Feed", + "feeds.select.interval": "Intervalo", + "feeds.start.on.load": "Iniciar ao carregar", + "feeds.tabs.download.rules": "Regras de Transferência", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Feeds de Torrent", + "feeds.tags": "Etiquetas", + "feeds.test.match": "Testar Padrão Correspondente", + "feeds.time.hr": "Horas", + "feeds.time.min": "Minutos", + "feeds.time.day": "Dias", + "feeds.torrent.destination": "Destino do Torrent", + "feeds.url": "URL", + "feeds.search": "Termo de pesquisa", + "feeds.search.term": "Termo de pesquisa", + "feeds.validation.invalid.regular.expression": "Expressão regular inválida.", + "feeds.validation.must.select.feed": "Precisa selecionar um feed.", + "feeds.validation.must.specify.destination": "Precisa especificar um destino.", + "feeds.validation.must.specify.label": "Precisa especificar uma etiqueta.", + "feeds.validation.must.specify.valid.feed.url": "Precisa especificar um URL de feed válido.", + "feeds.validation.interval.not.positive": "O intervalo deve ser um número inteiro positivo.", + "feeds.browse.feeds": "Procurar feeds", + "filesystem.empty.directory": "Diretório vazio.", + "filesystem.error.eacces": "O Flood não tem permissão para aceder a este diretório.", + "filesystem.error.enoent": "Este caminho não existe. Vai ser criado.", + "filesystem.error.unknown": "Ocorreu um erro desconhecido. Por favor, tente novamente.", + "filesystem.fetching": "Obtendo a estrutura de diretórios...", + "filesystem.parent.directory": "Diretório Superior", + "filter.all": "Todos", + "filter.status.title": "Filtrar por status", + "filter.status.downloading": "Transferindo", + "filter.status.seeding": "Semeando", + "filter.status.completed": "Concluído", + "filter.status.active": "Ativo", + "filter.status.inactive": "Inativo", + "filter.status.error": "Erro", + "filter.status.stopped": "Parado", + "filter.status.checking": "Verificando", + "filter.tracker.title": "Filtrar por Rastreador", + "filter.tag.title": "Filtrar por Tag", + "filter.untagged": "Sem etiqueta", + "general.ago": "atrás", + "general.at": "em", + "general.to": "para", + "general.of": "de", + "general.clipboard.copy": "Copiar", + "general.clipboard.copied": "Copiado", + "general.error.unknown": "Ocorreu um erro desconhecido", + "mediainfo.execError": "Ocorreu um erro durante a execução de mediainfo no servidor. Verifique se a mediainfo está instalada e disponível no PATH para Flood.", + "mediainfo.fetching": "Buscando...", + "mediainfo.heading": "Saída Mediainfo", + "notification.feed.torrent.added.heading": "Item do Feed na fila", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Download concluído", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Erro relatado", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Limpar Tudo", + "notification.showing": "Mostrando", + "priority.dont.download": "Não baixar", + "priority.high": "alta", + "priority.low": "baixa", + "priority.normal": "normal", + "settings.bandwidth.slots.download.global.label": "Baixar Slots Globais", + "settings.bandwidth.slots.download.label": "Baixar Slots por Torrent", + "settings.bandwidth.slots.heading": "Disponibilidade de Slot", + "settings.bandwidth.slots.upload.global.label": "Carregar Slots Globais", + "settings.bandwidth.slots.upload.label": "Carregar Slots por Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Predefinições Dropdown: Baixar", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Predefinições do Dropdown: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Taxa de Download Global", + "settings.bandwidth.transferrate.global.throttle.upload": "Taxa de envio global", + "settings.bandwidth.transferrate.heading": "Limite da Taxa de Transferência", + "settings.connectivity.dht.label": "Habilitar DHT", + "settings.connectivity.dht.port.label": "Porta DHT", + "settings.connectivity.dpd.heading": "Descoberta Descentralizada de Pares", + "settings.connectivity.incoming.heading": "Conexões de entrada", + "settings.connectivity.ip.hostname.label": "IP/Hostname reportado", + "settings.connectivity.max.http.connections": "Conexões HTTP máximas", + "settings.connectivity.peer.exchange.label": "Habilitar Troca de Pares", + "settings.connectivity.peers.desired.label": "Pares Desejados", + "settings.connectivity.peers.heading": "Pares", + "settings.connectivity.peers.max.label": "Máximo de Pares", + "settings.connectivity.peers.min.label": "Mínimo de Pares", + "settings.connectivity.peers.seeding.max.label": "Semente Máxima de Pares", + "settings.connectivity.peers.seeding.min.label": "Mínimo de Semeamento de Pares", + "settings.connectivity.port.open.label": "Porta Aberta", + "settings.connectivity.port.randomize.label": "Randomizar Porta", + "settings.connectivity.port.range.label": "Intervalo de Portas", + "settings.resources.disk.check.hash.label": "Verificar o Hash na Conclusão", + "settings.resources.disk.download.location.label": "Diretório de download padrão", + "settings.resources.disk.heading": "Disco", + "settings.resources.max.open.files": "Máximo de Arquivos Abertos", + "settings.resources.memory.heading": "Memória", + "settings.resources.memory.max.label": "Uso máximo de memória", + "settings.tabs.bandwidth": "Banda", + "settings.tabs.connectivity": "Conectividade", + "settings.tabs.heading": "Confirgurações", + "settings.tabs.resources": "Recursos", + "settings.tabs.authentication": "Autenticação", + "settings.tabs.userinterface": "Interface do usuário", + "settings.tabs.diskusage": "Uso do disco", + "settings.tabs.about": "SOBRE", + "settings.ui.locale": "Localidade", + "settings.ui.language": "IDIOMA", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Exibição de Lista de Torrent", + "settings.ui.torrent.size": "Tamanho do Torrent", + "settings.ui.torrent.size.expanded": "Vista Expandida", + "settings.ui.torrent.size.condensed": "Vista Condensada", + "settings.ui.torrent.details.enabled": "Ativado", + "settings.ui.torrent.details.tags.placement": "Na exibição expandida, as tags funcionam melhor no final da lista.", + "settings.ui.torrent.context.menu.items.show": "Apresentar", + "settings.ui.displayed.details": "Colunas de Detalhes do Torrent", + "settings.ui.displayed.context.menu.items": "Itens do Menu de Contexto", + "settings.diskusage.show": "Apresentar", + "settings.diskusage.mount.points": "Pontos de Uso de Disco", + "settings.about.flood": "Sobre Flood", + "sidebar.button.feeds": "Conteúdos", + "sidebar.button.settings": "Confirgurações", + "sidebar.button.speedlimits": "Limite de Velocidade", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Encerrar Sessão", + "sidebar.search.placeholder": "Procurar torrents", + "sidebar.transferdata.downloaded": "Baixado", + "sidebar.transferdata.uploaded": "Enviado", + "sidebar.speedlimits.download": "TRANSFERIR", + "sidebar.speedlimits.upload": "ENVIAR", + "speed.unlimited": "Ilimitado", + "unit.size.byte": "B", + "unit.size.kilobyte": "KB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "BR", + "unit.size.terabyte": "Tb", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "a", + "unit.time.week": "Semana", + "unit.time.day": "d", + "unit.time.hour": "HR", + "unit.time.minute": "min", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Adicionar Torrent", + "torrents.add.cookies.label": "Biscoitos", + "torrents.add.cookies.input.placeholder": "Nome opcional do cookie-=valor-cookie", + "torrents.add.destination.label": "Destino", + "torrents.add.destination.placeholder": "Destino", + "torrents.add.heading": "Adicionar Torrents", + "torrents.add.start.label": "Iniciar Torrent", + "torrents.add.tab.file.browse": "ou clique para navegar", + "torrents.add.tab.file.drop": "Solte alguns arquivos aqui,", + "torrents.add.tab.file.title": "Por Arquivo", + "torrents.add.tab.url.input.placeholder": "URL do Torrent ou Link Magnet", + "torrents.add.tab.url.title": "Por URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Crio", + "torrents.add.torrents.label": "Torrentes", + "torrents.add.tags": "Etiquetas", + "torrents.create.source.path.label": "fonte", + "torrents.create.trackers.label": "Rastreadores", + "torrents.create.tracker.input.placeholder": "URL do Rastreador", + "torrents.create.base.name.label": "Nome Base", + "torrents.create.base.name.input.placeholder": "Arquivo base opcional ou nome do diretório do torrent", + "torrents.create.comment.label": "Comentar", + "torrents.create.comment.input.placeholder": "Comentário opcional no arquivo torrent", + "torrents.create.info.source.label": "Fonte da Informação", + "torrents.create.info.source.input.placeholder": "Entrada de origem opcional em infohash", + "torrents.create.is.private.label": "Privado", + "torrents.create.tags.input.placeholder": "Tags em Flood. Não adicionado ao torrent criado.", + "torrents.destination.base_path": "Usar como caminho de base", + "torrents.destination.completed": "Concluído", + "torrents.details.actions.pause": "Suspender", + "torrents.details.actions.start": "Iniciar", + "torrents.details.actions.stop": "Interromper", + "torrents.details.details": "detalhes", + "torrents.details.files": "arquivos", + "torrents.details.files.loading": "Carregando detalhes do arquivo...", + "torrents.details.files.download.file": "{count, plural, =1 {Baixe o arquivo} other {Baixe arquivos}}", + "torrents.details.general.comment": "Comentar", + "torrents.details.general.connected": "{connected} conectado a {total}", + "torrents.details.general.date.added": "Adicionado", + "torrents.details.general.date.created": "Data de Criação", + "torrents.details.general.downloaded": "Baixado", + "torrents.details.general.free.disk.space": "Espaço em disco livre", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Gerais", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Rastreador", + "torrents.details.general.heading.transfer": "Transferência", + "torrents.details.general.location": "Local:", + "torrents.details.general.none": "Nenhuma", + "torrents.details.general.peers": "Pares", + "torrents.details.general.scheduler.ignored": "Ignorado", + "torrents.details.general.scheduler.obeyed": "Obedecido", + "torrents.details.general.scheduler": "Agendar", + "torrents.details.general.seeds": "Sementes", + "torrents.details.general.size": "Tamanho", + "torrents.details.general.tags": "Etiquetas", + "torrents.details.general.tracker.message": "Mensagem do Rastreador", + "torrents.details.general.type.private": "Privado", + "torrents.details.general.type.public": "Público", + "torrents.details.general.type": "tipo", + "torrents.details.mediainfo": "Informações de mídia", + "torrents.details.peers.no.data": "Não há dados de par para este torrent.", + "torrents.details.peers": "Pares", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} arquivo selecionado} other {{countElement} arquivos selecionados}}", + "torrents.details.selected.files.set.priority": "Definir prioridade", + "torrents.details.trackers.no.data": "Não há dados do rastreador para este torrent.", + "torrents.details.trackers.type": "tipo", + "torrents.details.trackers": "Rastreadores", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Limpar Filtros", + "torrents.list.context.check.hash": "Verificar o Hash", + "torrents.list.context.details": "Detalhes do Torrent", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Definir local do Torrent", + "torrents.list.context.pause": "Suspender", + "torrents.list.context.download": "Transferir", + "torrents.list.context.priority": "Prioridade", + "torrents.list.context.remove": "Excluir", + "torrents.list.context.set.tags": "Definir Tags", + "torrents.list.context.set.trackers": "Definir Rastreadores", + "torrents.list.context.start": "Iniciar", + "torrents.list.context.stop": "Interromper", + "torrents.list.no.torrents": "Não há torrents para exibir.", + "torrents.list.drop": "Arraste arquivos aqui para adicioná-los.", + "torrents.list.cannot.connect": "Não é possível conectar-se ao cliente.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "de", + "torrents.move.button.set.location": "Definir Local", + "torrents.move.button.state.setting": "Configuração...", + "torrents.move.data.label": "Mover dados", + "torrents.move.check_hash.label": "Verificar o hash", + "torrents.move.heading": "Definir local do Torrent", + "torrents.properties.date.added": "Adicionado", + "torrents.properties.comment": "Comentar", + "torrents.properties.creation.date": "Data de Criação", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Velocidade de Download", + "torrents.properties.download.total": "Baixado", + "torrents.properties.eta": "Tempo Estimado", + "torrents.properties.free.disk.space": "Espaço em disco livre", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignorar o Agendador", + "torrents.properties.is.private": "Privado", + "torrents.properties.name": "Nome:", + "torrents.properties.percentage": "Porcentagem Completa", + "torrents.properties.ratio": "Tarifa", + "torrents.properties.size": "Tamanho do arquivo", + "torrents.properties.tags": "Etiquetas", + "torrents.properties.tracker.message": "Mensagem do Rastreador", + "torrents.properties.upload.speed": "Velocidade de Upload", + "torrents.properties.upload.total": "Enviado", + "torrents.properties.seeds": "Sementes", + "torrents.properties.peers": "Pares", + "torrents.properties.trackers": "Rastreadores", + "torrents.remove.are.you.sure": "Você tem certeza que quer remover {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Apagar dados", + "torrents.remove.error.no.torrents.selected": "Você não selecionou nenhum torrents.", + "torrents.remove": "Remover Torrents", + "torrents.set.tags.button.set": "Definir Tags", + "torrents.set.tags.heading": "Definir Tags", + "torrents.set.tags.enter.tags": "Insira tags", + "torrents.set.trackers.button.set": "Definir Rastreadores", + "torrents.set.trackers.heading": "Definir Rastreadores", + "torrents.set.trackers.enter.tracker": "Digite um rastreador", + "torrents.set.trackers.loading.trackers": "Carregando rastreadores...", + "torrents.sort.title": "Classificar por", + "connection-interruption.heading": "Não é possível conectar-se ao cliente", + "status.diskusage.title": "Uso do disco", + "status.diskusage.used": "Utilizado", + "status.diskusage.free": "Livre", + "status.diskusage.total": "Total", + "locale.language.auto": "Automático", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notificações", + "dependency.loading.torrent.taxonomy": "Taxonomia do Torrent", + "dependency.loading.transfer.rate.details": "Detalhes da Taxa de Transferência de Dados", + "dependency.loading.transfer.history": "Histórico de Transferências de Dados", + "dependency.loading.torrent.list": "Lista de Torrents" +} diff --git a/client/src/javascript/i18n/translations/ro.json b/client/src/javascript/i18n/translations/ro.json new file mode 100644 index 000000000..25f3703f1 --- /dev/null +++ b/client/src/javascript/i18n/translations/ro.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Pornește torentul", + "actionbar.button.stop.torrent": "Oprește torentul", + "actionbar.button.add.torrent": "Adaugă torent", + "actionbar.button.remove.torrent": "Șterge torentul", + "alert.torrent.add": "{count, plural, =1 {A fost adăugat} other {Au fost adăugate}} cu succes {countElement} {count, plural, =1 {torent} other {torente}}.", + "alert.torrent.add.failed": "Nu {count, plural, =1 {s-a} other {s-au}} putut adăuga {countElement} {count, plural, =1 {torent} other {torente}}.", + "alert.torrent.move": "{count, plural, =1 {A fost mutat} other {Au fost mutate}} cu succes {countElement} {count, plural, =1 {torent} other {torente}}.", + "alert.torrent.move.failed": "Nu {count, plural, =1 {s-a} other {s-au}} mutat {countElement} {count, plural, =1 {torent} other {torente}}.", + "alert.torrent.remove": "{count, plural, =1 {A fost șters} other {Au fost șterse}} cu succes {countElement} {count, plural, =1 {torent} other {torente}}.", + "alert.torrent.remove.failed": "Nu {count, plural, =1 {s-a} other {s-au}} șters {countElement} {count, plural, =1 {torent} other {torente}}.", + "alert.settings.saved": "Setări salvate cu succes.", + "auth.add.user": "Adaugă utilizator", + "auth.create.account": "Creează cont", + "auth.create.an.account": "Creează un cont", + "auth.create.an.account.intro": "Bine ai venit la Flood!", + "auth.current.user": "Utilizator curent", + "auth.error.username.empty": "Numele de utilizator nu poate fi gol.", + "auth.error.password.empty": "Parola nu poate fi goală.", + "auth.input.clear": "Șterge", + "auth.log.in": "Autentificare", + "auth.login": "Autentificare", + "auth.login.intro": "Conectează-te la contul tău.", + "auth.password": "Parolă", + "auth.user.accounts": "Conturi de utilizator", + "auth.username": "Nume de utilizator", + "auth.admin": "Admin", + "auth.message.not.admin": "Utilizatorul nu este Administrator", + "button.add": "Adaugă", + "button.cancel": "Anulează", + "button.close": "Închide", + "button.download": "Descarcă", + "button.no": "Nu", + "button.ok": "OK", + "button.retry": "Încearcă din nou", + "button.save": "Salvează setările", + "button.save.feed": "Salvează", + "button.state.adding": "Se adaugă...", + "button.yes": "Da", + "button.new": "Nou", + "connection-interruption.action.selection.retry": "Încearcă din nou cu setările curente ale clientului", + "connection-interruption.action.selection.config": "Actualizează setările de conexiune ale clientului", + "connection-interruption.not.admin": "Te rog contactează administratorul tău Flood dacă această eroare persistă.", + "connection-interruption.verification-error": "Conexiunea nu a putut fi verificată.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Setările conexiunii nu pot fi goale.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Tipul conexiunii", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Expunerea rTorrent prin TCP poate permite creșterea privilegiilor.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Nume host sau IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Cale", + "connection.settings.rtorrent.socket.input.placeholder": "Calea către socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "Adresă", + "connection.settings.qbittorrent.url.input.placeholder": "Adresă către qBittorrent Web API", + "connection.settings.qbittorrent.username": "Nume", + "connection.settings.qbittorrent.username.input.placeholder": "Nume", + "connection.settings.qbittorrent.password": "Parolă", + "connection.settings.qbittorrent.password.input.placeholder": "Parolă", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "Adresă", + "connection.settings.transmission.url.input.placeholder": "Adresă către interfața Transmission RPC", + "connection.settings.transmission.username": "Utilizator", + "connection.settings.transmission.username.input.placeholder": "Utilizator", + "connection.settings.transmission.password": "Parolă", + "connection.settings.transmission.password.input.placeholder": "Parolă", + "connectivity.modal.title": "Problemă de conectivitate", + "connectivity.modal.content": "Nu se poate conecta la client. Actualizează setările conexiunii.", + "feeds.add.automatic.download.rule": "Adaugă regulă de descărcare", + "feeds.add.feed": "Adaugă flux", + "feeds.applicable.feed": "Flux aplicabil", + "feeds.apply.tags": "Aplică etichete", + "feeds.check": "Validează regula încercând-o. Nu a fost salvată sau trimisă.", + "feeds.exclude.pattern": "Exclude modelul", + "feeds.existing.feeds": "Fluxuri existente", + "feeds.existing.rules": "Reguli existente", + "feeds.interval": "Interval", + "feeds.label": "Etichetă", + "feeds.match.count": "{count, plural, =1 {# potrivire} other {# potriviri}}", + "feeds.match.pattern": "Potrivire model", + "feeds.match": "Potrivire", + "feeds.exclude": "Exclude", + "feeds.no.feeds.available": "Nici un flux disponibil.", + "feeds.no.feeds.defined": "Nici un flux definit.", + "feeds.no.items.matching": "Nu există elemente care să corespundă termenului de căutare.", + "feeds.no.rules.defined": "Nici o regulă definită.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Selectează flux", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Începe la încărcare", + "feeds.tabs.download.rules": "Reguli de descărcare", + "feeds.tabs.feeds": "Fluxuri", + "feeds.tabs.heading": "Fluxuri de torent", + "feeds.tags": "Etichete", + "feeds.test.match": "Testează modelul de potrivire", + "feeds.time.hr": "Ore", + "feeds.time.min": "Minute", + "feeds.time.day": "Zile", + "feeds.torrent.destination": "Destinația torentului", + "feeds.url": "Adresă", + "feeds.search": "Termen de căutare", + "feeds.search.term": "Termen de căutare", + "feeds.validation.invalid.regular.expression": "Expresie regulată invalidă.", + "feeds.validation.must.select.feed": "Trebuie să selectezi un flux.", + "feeds.validation.must.specify.destination": "Trebuie să specifici o destinație.", + "feeds.validation.must.specify.label": "Trebuie să specifici o etichetă.", + "feeds.validation.must.specify.valid.feed.url": "Trebuie să specifici o adresă de flux validă.", + "feeds.validation.interval.not.positive": "Intervalul trebuie să fie un număr întreg pozitiv.", + "feeds.browse.feeds": "Răsfoiește fluxuri", + "filesystem.empty.directory": "Dosar gol.", + "filesystem.error.eacces": "Flood nu are permisiunea de a citi acest director.", + "filesystem.error.enoent": "Această cale nu există. Va fi creată.", + "filesystem.error.unknown": "A apărut o eroare necunoscută. Te rugăm să încerci din nou.", + "filesystem.fetching": "Se preia structura directorului...", + "filesystem.parent.directory": "Director părinte", + "filter.all": "Toate", + "filter.status.title": "Filtrare după stare", + "filter.status.downloading": "Descărcând", + "filter.status.seeding": "Donând", + "filter.status.completed": "Complet", + "filter.status.active": "Activ", + "filter.status.inactive": "Inactiv", + "filter.status.error": "Eroare", + "filter.status.stopped": "Oprit", + "filter.status.checking": "Se verifică", + "filter.tracker.title": "Filtrează după tracker", + "filter.tag.title": "Filtrare după etichetă", + "filter.untagged": "Neetichetat", + "general.ago": "acum", + "general.at": "la", + "general.to": "către", + "general.of": "din", + "general.clipboard.copy": "Copie", + "general.clipboard.copied": "Copiat", + "general.error.unknown": "A apărut o eroare necunoscută", + "mediainfo.execError": "A apărut o eroare în timpul executării mediainfo pe server. Verificați dacă mediainfo este instalat și disponibil în CALEA către Flood.", + "mediainfo.fetching": "Se preia...", + "mediainfo.heading": "Ieșire mediainfo", + "notification.feed.torrent.added.heading": "Element din flux în așteptare", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "Nicio notificare de afișat.", + "notification.torrent.finished.heading": "Descărcare finalizată", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Eroare raportată", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Șterge tot", + "notification.showing": "Afișare", + "priority.dont.download": "Nu descărca", + "priority.high": "Ridicată", + "priority.low": "Scazută", + "priority.normal": "Normală", + "settings.bandwidth.slots.download.global.label": "Sloturi globale pentru descărcare", + "settings.bandwidth.slots.download.label": "Sloturi pentru descărcare per torent", + "settings.bandwidth.slots.heading": "Disponibilitatea sloturilor", + "settings.bandwidth.slots.upload.global.label": "Sloturi globale pentru încărcare", + "settings.bandwidth.slots.upload.label": "Sloturi pentru încărcare per torent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Presetări dropdown: Descărcare", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Presetări dropdown: Încărcare", + "settings.bandwidth.transferrate.global.throttle.download": "Rata globală de descărcare", + "settings.bandwidth.transferrate.global.throttle.upload": "Rata globală de încărcare", + "settings.bandwidth.transferrate.heading": "Rata de transfer", + "settings.connectivity.dht.label": "Activează DHT", + "settings.connectivity.dht.port.label": "Port DHT", + "settings.connectivity.dpd.heading": "Descoperirea descentralizată a partenerilor", + "settings.connectivity.incoming.heading": "Conexiuni de intrare", + "settings.connectivity.ip.hostname.label": "IP/Hostname raportat", + "settings.connectivity.max.http.connections": "Conexiuni HTTP maxime", + "settings.connectivity.peer.exchange.label": "Activează schimbul de parteneri", + "settings.connectivity.peers.desired.label": "Parteneri dornici", + "settings.connectivity.peers.heading": "Parteneri", + "settings.connectivity.peers.max.label": "Nr. maxim de parteneri", + "settings.connectivity.peers.min.label": "Nr. minim de parteneri", + "settings.connectivity.peers.seeding.max.label": "Nr. maxim de parteneri donatori", + "settings.connectivity.peers.seeding.min.label": "Nr. minim de parteneri donatori", + "settings.connectivity.port.open.label": "Port deschis", + "settings.connectivity.port.randomize.label": "Port aleator", + "settings.connectivity.port.range.label": "Interval port", + "settings.resources.disk.check.hash.label": "Verifică hash-ul la finalizare", + "settings.resources.disk.download.location.label": "Directorul implicit pentru descărcări", + "settings.resources.disk.heading": "Disc", + "settings.resources.max.open.files": "Nr. maxim de fişiere deschise", + "settings.resources.memory.heading": "Memorie", + "settings.resources.memory.max.label": "Utilizarea maximă a memoriei", + "settings.tabs.bandwidth": "Lățime de bandă", + "settings.tabs.connectivity": "Conectivitate", + "settings.tabs.heading": "Setări", + "settings.tabs.resources": "Resurse", + "settings.tabs.authentication": "Autentificare", + "settings.tabs.userinterface": "Interfața cu utilizatorul", + "settings.tabs.diskusage": "Utilizare disc", + "settings.tabs.about": "Despre", + "settings.ui.locale": "Localizare", + "settings.ui.language": "Limba", + "settings.ui.tag.selector.mode": "Preferința selectorului de etichete", + "settings.ui.tag.selector.mode.single": "Selecție unică", + "settings.ui.tag.selector.mode.multi": "Selecție multiplă", + "settings.ui.torrent.list": "Afișare listă torente", + "settings.ui.torrent.size": "Mărimea torentului", + "settings.ui.torrent.size.expanded": "Vedere extinsă", + "settings.ui.torrent.size.condensed": "Vedere compactă", + "settings.ui.torrent.details.enabled": "Activat", + "settings.ui.torrent.details.tags.placement": "În vederea extinsă, etichetele funcționează cel mai bine la sfârșitul listei.", + "settings.ui.torrent.context.menu.items.show": "Arată", + "settings.ui.displayed.details": "Coloane cu detaliile torentelor", + "settings.ui.displayed.context.menu.items": "Elementele meniu contextual", + "settings.diskusage.show": "Arată", + "settings.diskusage.mount.points": "Puncte de montare pentru utilizarea discului", + "settings.about.flood": "Despre Flood", + "sidebar.button.feeds": "Fluxuri", + "sidebar.button.settings": "Setări", + "sidebar.button.speedlimits": "Limite de viteză", + "sidebar.button.theme.dark": "Temă întunecată", + "sidebar.button.theme.light": "Temă luminată", + "sidebar.button.log.out": "Ieșire din cont", + "sidebar.search.placeholder": "Caută torente", + "sidebar.transferdata.downloaded": "Descărcat", + "sidebar.transferdata.uploaded": "Încărcat", + "sidebar.speedlimits.download": "DESCĂRCARE", + "sidebar.speedlimits.upload": "ÎNCĂRCARE", + "speed.unlimited": "Nelimitat", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "oră", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Adaugă torent", + "torrents.add.cookies.label": "Cookie-uri", + "torrents.add.cookies.input.placeholder": "cookie-name = cookie-value", + "torrents.add.destination.label": "Destinație", + "torrents.add.destination.placeholder": "Destinație", + "torrents.add.heading": "Adaugă torente", + "torrents.add.start.label": "Pornește torentul", + "torrents.add.tab.file.browse": "sau fă clic pentru a naviga", + "torrents.add.tab.file.drop": "Pune câteva fișiere aici,", + "torrents.add.tab.file.title": "După fișier", + "torrents.add.tab.url.input.placeholder": "Adresa torentului sau adresa magnet", + "torrents.add.tab.url.title": "După adresă", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Crează", + "torrents.add.torrents.label": "Torente", + "torrents.add.tags": "Etichete", + "torrents.create.source.path.label": "Sursa", + "torrents.create.trackers.label": "Trackere", + "torrents.create.tracker.input.placeholder": "Adresă tracker", + "torrents.create.base.name.label": "Nume de bază", + "torrents.create.base.name.input.placeholder": "Numele fișierului de bază sau numele directorului torentului", + "torrents.create.comment.label": "Comentariu", + "torrents.create.comment.input.placeholder": "Comentariu în fișierul torent", + "torrents.create.info.source.label": "Sursa de informații", + "torrents.create.info.source.input.placeholder": "Intrare sursă în infohash", + "torrents.create.is.private.label": "Privat", + "torrents.create.tags.input.placeholder": "Etichete în Flood. Nu se adăugă la torentul creat.", + "torrents.destination.base_path": "Folosește ca și cale de bază", + "torrents.destination.completed": "Finalizat", + "torrents.details.actions.pause": "Întrerupe", + "torrents.details.actions.start": "Pornește", + "torrents.details.actions.stop": "Oprește", + "torrents.details.details": "Detalii", + "torrents.details.files": "Fișiere", + "torrents.details.files.loading": "Se încarcă detaliile fișierului...", + "torrents.details.files.download.file": "{count, plural, =1 {Descarcă fişierul} other {Descarcă fişierele}}", + "torrents.details.general.comment": "Comentariu", + "torrents.details.general.connected": "{connected} conectați din {total}", + "torrents.details.general.date.added": "Adăugat", + "torrents.details.general.date.created": "Data creării", + "torrents.details.general.downloaded": "Descărcat", + "torrents.details.general.free.disk.space": "Spațiu liber pe disc", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Generalități", + "torrents.details.general.heading.torrent": "Torent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfer", + "torrents.details.general.location": "Locație", + "torrents.details.general.none": "Niciunul", + "torrents.details.general.peers": "Parteneri", + "torrents.details.general.scheduler.ignored": "Ignorat", + "torrents.details.general.scheduler.obeyed": "Respectat", + "torrents.details.general.scheduler": "Planificator", + "torrents.details.general.seeds": "Parteneri", + "torrents.details.general.size": "Dimensiune", + "torrents.details.general.tags": "Etichete", + "torrents.details.general.tracker.message": "Mesaj tracker", + "torrents.details.general.type.private": "Privat", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Tip", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Nu există date despre parteneri pentru acest torent.", + "torrents.details.peers": "Parteneri", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} fișier selectat} other {{countElement} fișiere selectate}}", + "torrents.details.selected.files.set.priority": "Setează prioritate", + "torrents.details.trackers.no.data": "Nu există date despre tracker pentru acest torent.", + "torrents.details.trackers.type": "Tip", + "torrents.details.trackers": "Trackere", + "torrents.generate.magnet.heading": "Generează adresă magnet", + "torrents.generate.magnet.loading.trackers": "Se încarcă trackerele...", + "torrents.generate.magnet.private.torrent": "Acesta este un torent privat.", + "torrents.generate.magnet.magnet": "Adresă magnet", + "torrents.generate.magnet.magnet.with.trackers": "Adresă magnet cu trackere", + "torrents.list.clear.filters": "Șterge filtrele", + "torrents.list.context.check.hash": "Verifică hash-ul", + "torrents.list.context.details": "Detalii torent", + "torrents.list.context.generate.magnet": "Generează adresă magnet", + "torrents.list.context.move": "Setează locația torentului", + "torrents.list.context.pause": "Întrerupe", + "torrents.list.context.download": "Descarcă", + "torrents.list.context.priority": "Prioritate", + "torrents.list.context.remove": "Șterge", + "torrents.list.context.set.tags": "Setează etichete", + "torrents.list.context.set.trackers": "Setează trackere", + "torrents.list.context.start": "Pornește", + "torrents.list.context.stop": "Oprește", + "torrents.list.no.torrents": "Nu sunt torente de afișat.", + "torrents.list.drop": "Plasați fișierele aici pentru a le adăuga.", + "torrents.list.cannot.connect": "Nu se poate conecta la client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "din", + "torrents.move.button.set.location": "Setează locația", + "torrents.move.button.state.setting": "Se setează...", + "torrents.move.data.label": "Mută datele", + "torrents.move.check_hash.label": "Verifică hash-ul", + "torrents.move.heading": "Setează locația torentului", + "torrents.properties.date.added": "Adăugat", + "torrents.properties.comment": "Comentariu", + "torrents.properties.creation.date": "Data creării", + "torrents.properties.directory": "Locație", + "torrents.properties.download.speed": "Viteza de descărcare", + "torrents.properties.download.total": "Descărcat", + "torrents.properties.eta": "Timp rămas", + "torrents.properties.free.disk.space": "Spațiu liber pe disc", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignoră planificatorul", + "torrents.properties.is.private": "Privat", + "torrents.properties.name": "Nume", + "torrents.properties.percentage": "Procentaj completare", + "torrents.properties.ratio": "Raport", + "torrents.properties.size": "Dimensiune fișier", + "torrents.properties.tags": "Etichete", + "torrents.properties.tracker.message": "Mesajul trackerului", + "torrents.properties.upload.speed": "Viteza de încărcare", + "torrents.properties.upload.total": "Încărcat", + "torrents.properties.seeds": "Donatori", + "torrents.properties.peers": "Parteneri", + "torrents.properties.trackers": "Trackere", + "torrents.remove.are.you.sure": "Ești sigur că vrei să ștergi {count, plural, =1 {# torent} other {# torrente}}?", + "torrents.remove.delete.data": "Ștergere datele", + "torrents.remove.error.no.torrents.selected": "Nu ai selectat niciun torent.", + "torrents.remove": "Șterge torentele", + "torrents.set.tags.button.set": "Setează etichete", + "torrents.set.tags.heading": "Setare etichete", + "torrents.set.tags.enter.tags": "Introduceți etichetele", + "torrents.set.trackers.button.set": "Setează trackere", + "torrents.set.trackers.heading": "Setare trackere", + "torrents.set.trackers.enter.tracker": "Introdu un tracker", + "torrents.set.trackers.loading.trackers": "Se încarcă trackerele...", + "torrents.sort.title": "Sortează după", + "connection-interruption.heading": "Nu se poate conecta la client", + "status.diskusage.title": "Utilizare disc", + "status.diskusage.used": "Utilizat", + "status.diskusage.free": "Liber", + "status.diskusage.total": "Total", + "locale.language.auto": "Automat", + "locale.language.translate": "Ajută-ne să traducem", + "dependency.loading.notifications": "Notificări", + "dependency.loading.torrent.taxonomy": "Taxonomia torentului", + "dependency.loading.transfer.rate.details": "Detaliile ratei de transfer ale datelor", + "dependency.loading.transfer.history": "Istoricul transferurilor de date", + "dependency.loading.torrent.list": "Lista de torente" +} diff --git a/client/src/javascript/i18n/translations/ru.json b/client/src/javascript/i18n/translations/ru.json new file mode 100644 index 000000000..1f8551281 --- /dev/null +++ b/client/src/javascript/i18n/translations/ru.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Начать торрент", + "actionbar.button.stop.torrent": "Остановить торрент", + "actionbar.button.add.torrent": "Добавить торрент", + "actionbar.button.remove.torrent": "Удалить торрент", + "alert.torrent.add": "Успешно добавлено {countElement} {count, plural, =1 {} other {торрентов}}", + "alert.torrent.add.failed": "Не удалось добавить {countElement} {count, plural, =1 {торрент} other {торрентов}}", + "alert.torrent.move": "Успешно перемещено {countElement} {count, plural, =1 {} other {торрентов}}", + "alert.torrent.move.failed": "Не удалось переместить {countElement} {count, plural, =1 {} other {торрентов}}", + "alert.torrent.remove": "Удалено {countElement} {count, plural, =1 {торрент} other {торрентов}}", + "alert.torrent.remove.failed": "Не удалось удалить {countElement} {count, plural, =1 {торрент} other {торрентов}}", + "alert.settings.saved": "Настройки успешно сохранены.", + "auth.add.user": "Добавить пользователя", + "auth.create.account": "Создать Аккаунт", + "auth.create.an.account": "Создать аккаунт", + "auth.create.an.account.intro": "Добро пожаловать в Flood!", + "auth.current.user": "Текущий пользователь", + "auth.error.username.empty": "Имя пользователя не может быть пустым.", + "auth.error.password.empty": "Пароль не может быть пустым.", + "auth.input.clear": "Clear", + "auth.log.in": "Войти", + "auth.login": "Логин", + "auth.login.intro": "Войдите в свою учетную запись.", + "auth.password": "Пароль", + "auth.user.accounts": "Учетные записи пользователей", + "auth.username": "Имя пользователя", + "auth.admin": "Админ", + "auth.message.not.admin": "Пользователь не является администратором", + "button.add": "Добавить", + "button.cancel": "Отмена", + "button.close": "Close", + "button.download": "Скачать", + "button.no": "Нет", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Сохранить настройки", + "button.save.feed": "Сохранить", + "button.state.adding": "Добавление...", + "button.yes": "Да", + "button.new": "Новый", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Соединение не может быть проверено.", + "connection.settings.client.select": "Клиент", + "connection.settings.error.empty": "Настройки соединения не могут быть пустыми.", + "connection.settings.rtorrent": "рторрент", + "connection.settings.rtorrent.type": "Тип соединения", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Хост", + "connection.settings.rtorrent.host.input.placeholder": "Имя хоста или IP", + "connection.settings.rtorrent.port": "Порт", + "connection.settings.rtorrent.port.input.placeholder": "Порт", + "connection.settings.rtorrent.socket": "Путь", + "connection.settings.rtorrent.socket.input.placeholder": "Путь к сокету", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL qBittorrent Web API", + "connection.settings.qbittorrent.username": "Имя пользователя", + "connection.settings.qbittorrent.username.input.placeholder": "Имя пользователя", + "connection.settings.qbittorrent.password": "Пароль", + "connection.settings.qbittorrent.password.input.placeholder": "Пароль", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Проблема с подключением", + "connectivity.modal.content": "Не удается подключиться к клиенту. Пожалуйста, обновите настройки соединения.", + "feeds.add.automatic.download.rule": "Добавить правило загрузки", + "feeds.add.feed": "Добавить канал", + "feeds.applicable.feed": "Применимая лента", + "feeds.apply.tags": "Применить теги", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Исключить шаблон", + "feeds.existing.feeds": "Существующие каналы", + "feeds.existing.rules": "Существующие правила", + "feeds.interval": "Интервал", + "feeds.label": "Метка", + "feeds.match.count": "{count, plural, =1 {# совпадает} other {# совпадает}}", + "feeds.match.pattern": "Совпадение с шаблоном", + "feeds.match": "Матч", + "feeds.exclude": "Исключить", + "feeds.no.feeds.available": "Нет доступных лент.", + "feeds.no.feeds.defined": "Каналы не определены.", + "feeds.no.items.matching": "Нет элементов, соответствующих условиям поиска.", + "feeds.no.rules.defined": "Правила не определены.", + "feeds.regEx": "РегЭкс", + "feeds.select.feed": "Выбрать ленту", + "feeds.select.interval": "Интервал", + "feeds.start.on.load": "Запуск при загрузке", + "feeds.tabs.download.rules": "Правила загрузки", + "feeds.tabs.feeds": "Ленты", + "feeds.tabs.heading": "Каналы торрентов", + "feeds.tags": "Теги", + "feeds.test.match": "Тест соответствия шаблону", + "feeds.time.hr": "Часы", + "feeds.time.min": "Минут", + "feeds.time.day": "Дней", + "feeds.torrent.destination": "Назначение торрента", + "feeds.url": "URL", + "feeds.search": "Поисковый термин", + "feeds.search.term": "Поисковый термин", + "feeds.validation.invalid.regular.expression": "Неверное регулярное выражение.", + "feeds.validation.must.select.feed": "Вы должны выбрать канал.", + "feeds.validation.must.specify.destination": "Вы должны указать место назначения.", + "feeds.validation.must.specify.label": "Необходимо указать метку.", + "feeds.validation.must.specify.valid.feed.url": "Вы должны указать корректный URL канала.", + "feeds.validation.interval.not.positive": "Интервал должен быть положительным целым числом.", + "feeds.browse.feeds": "Просмотр каналов", + "filesystem.empty.directory": "Пустой каталог.", + "filesystem.error.eacces": "Flood не имеет разрешения на чтение этого каталога.", + "filesystem.error.enoent": "Этот путь не существует. Он будет создан.", + "filesystem.error.unknown": "Произошла неизвестная ошибка. Пожалуйста, попробуйте еще раз.", + "filesystem.fetching": "Получение структуры директорий...", + "filesystem.parent.directory": "Родительская папка", + "filter.all": "Все", + "filter.status.title": "Фильтр по статусу", + "filter.status.downloading": "Скачивание", + "filter.status.seeding": "Раздача", + "filter.status.completed": "Complete", + "filter.status.active": "Активный", + "filter.status.inactive": "Неактивный", + "filter.status.error": "Ошибка", + "filter.status.stopped": "Остановлено", + "filter.status.checking": "Проверка", + "filter.tracker.title": "Фильтровать по трекеру", + "filter.tag.title": "Фильтр по тегу", + "filter.untagged": "Без тегов", + "general.ago": "назад", + "general.at": "в", + "general.to": "до", + "general.of": "из", + "general.clipboard.copy": "Копировать", + "general.clipboard.copied": "Скопировано", + "general.error.unknown": "Произошла неизвестная ошибка", + "mediainfo.execError": "Произошла ошибка при запуске mediainfo на сервере. Убедитесь, что mediainfo установлен и доступен в PATH для Flood.", + "mediainfo.fetching": "Извлечение...", + "mediainfo.heading": "Вывод Mediainfo", + "notification.feed.torrent.added.heading": "Предмет в очереди", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Загрузка завершена", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Сообщение об ошибке", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Очистить все", + "notification.showing": "Показано", + "priority.dont.download": "Не загружать", + "priority.high": "Высокий", + "priority.low": "Низкий", + "priority.normal": "Обычный", + "settings.bandwidth.slots.download.global.label": "Скачать слоты по всему", + "settings.bandwidth.slots.download.label": "Скачать слоты на торрент", + "settings.bandwidth.slots.heading": "Доступность слота", + "settings.bandwidth.slots.upload.global.label": "Глобальная загрузка слотов", + "settings.bandwidth.slots.upload.label": "Загрузить слоты на торрент", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Шаблоны комбобоксов: Скачать", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Шаблоны комбобоксов: Загрузить", + "settings.bandwidth.transferrate.global.throttle.download": "Глобальная скорость загрузки", + "settings.bandwidth.transferrate.global.throttle.upload": "Глобальный тротттл скорости загрузки", + "settings.bandwidth.transferrate.heading": "Процент трансфертов", + "settings.connectivity.dht.label": "Включить DHT", + "settings.connectivity.dht.port.label": "DHT порт", + "settings.connectivity.dpd.heading": "Децентрализованное обнаружение пиров", + "settings.connectivity.incoming.heading": "Входящие соединения", + "settings.connectivity.ip.hostname.label": "Сообщил IP/имя хоста", + "settings.connectivity.max.http.connections": "Максимум HTTP-соединений", + "settings.connectivity.peer.exchange.label": "Включить обмен пирами", + "settings.connectivity.peers.desired.label": "Желаемые узлы", + "settings.connectivity.peers.heading": "Личеры", + "settings.connectivity.peers.max.label": "Максимум пиров", + "settings.connectivity.peers.min.label": "Минимум пиров", + "settings.connectivity.peers.seeding.max.label": "Максимум раздачи пиров", + "settings.connectivity.peers.seeding.min.label": "Минимум раздачи пиров", + "settings.connectivity.port.open.label": "Открыть порт", + "settings.connectivity.port.randomize.label": "Случайный порт", + "settings.connectivity.port.range.label": "Диапазон портов", + "settings.resources.disk.check.hash.label": "Проверить хэш при завершении", + "settings.resources.disk.download.location.label": "Папка загрузок по умолчанию", + "settings.resources.disk.heading": "Диск", + "settings.resources.max.open.files": "Максимальное количество открытых файлов", + "settings.resources.memory.heading": "Память", + "settings.resources.memory.max.label": "Максимальное использование памяти", + "settings.tabs.bandwidth": "Трафик", + "settings.tabs.connectivity": "Подключение", + "settings.tabs.heading": "Настройки", + "settings.tabs.resources": "Ресурсы", + "settings.tabs.authentication": "Проверка подлинности", + "settings.tabs.userinterface": "Интерфейс пользователя", + "settings.tabs.diskusage": "Использование диска", + "settings.tabs.about": "О программе", + "settings.ui.locale": "Язык", + "settings.ui.language": "Язык", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Отображение списка торрентов", + "settings.ui.torrent.size": "Размер торрента", + "settings.ui.torrent.size.expanded": "Расширенный вид", + "settings.ui.torrent.size.condensed": "Сжатый вид", + "settings.ui.torrent.details.enabled": "Включено", + "settings.ui.torrent.details.tags.placement": "В расширенном виде теги работают лучше в конце списка.", + "settings.ui.torrent.context.menu.items.show": "Показать", + "settings.ui.displayed.details": "Детали Торрента", + "settings.ui.displayed.context.menu.items": "Элементы контекстного меню", + "settings.diskusage.show": "Показать", + "settings.diskusage.mount.points": "Точки монтирования диска", + "settings.about.flood": "О Flood", + "sidebar.button.feeds": "Ленты", + "sidebar.button.settings": "Настройки", + "sidebar.button.speedlimits": "Ограничения скорости", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Выйти", + "sidebar.search.placeholder": "Поиск торрентов", + "sidebar.transferdata.downloaded": "Загружено", + "sidebar.transferdata.uploaded": "Загружено", + "sidebar.speedlimits.download": "СКАЧАТЬ", + "sidebar.speedlimits.upload": "ЗАГРУЗИТЬ", + "speed.unlimited": "Неограниченный", + "unit.size.byte": "В", + "unit.size.kilobyte": "кБ", + "unit.size.megabyte": "МБ", + "unit.size.gigabyte": "Гб", + "unit.size.terabyte": "ТБ", + "unit.speed": "{baseUnit}/с", + "unit.time.year": "yr", + "unit.time.week": "ск", + "unit.time.day": "д", + "unit.time.hour": "час", + "unit.time.minute": "м", + "unit.time.second": "с", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Добавить торрент", + "torrents.add.cookies.label": "Печенье", + "torrents.add.cookies.input.placeholder": "Необязательные cookie-name=cookie-значение", + "torrents.add.destination.label": "Назначение", + "torrents.add.destination.placeholder": "Назначение", + "torrents.add.heading": "Добавить торренты", + "torrents.add.start.label": "Начать торрент", + "torrents.add.tab.file.browse": "или нажмите для просмотра", + "torrents.add.tab.file.drop": "Перетащите файлы сюда", + "torrents.add.tab.file.title": "По файлу", + "torrents.add.tab.url.input.placeholder": "Торрент URL или Magnet ссылка", + "torrents.add.tab.url.title": "По URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Создать", + "torrents.add.torrents.label": "Торренты", + "torrents.add.tags": "Теги", + "torrents.create.source.path.label": "Источник", + "torrents.create.trackers.label": "Трекеры", + "torrents.create.tracker.input.placeholder": "URL трекера", + "torrents.create.base.name.label": "Базовое имя", + "torrents.create.base.name.input.placeholder": "Необязательный базовый файл или имя каталога торрента", + "torrents.create.comment.label": "Комментарий", + "torrents.create.comment.input.placeholder": "Дополнительный комментарий в торрент-файле", + "torrents.create.info.source.label": "Источник информации", + "torrents.create.info.source.input.placeholder": "Необязательная запись в infohash", + "torrents.create.is.private.label": "Приватный", + "torrents.create.tags.input.placeholder": "Теги в потопе. Не добавлены в торрент.", + "torrents.destination.base_path": "Использовать как базовый путь", + "torrents.destination.completed": "Выполнено", + "torrents.details.actions.pause": "Пауза", + "torrents.details.actions.start": "Начать", + "torrents.details.actions.stop": "Остановить", + "torrents.details.details": "Детали", + "torrents.details.files": "Файлы", + "torrents.details.files.loading": "Загрузка деталей файла...", + "torrents.details.files.download.file": "{count, plural, =1 {Скачайте файл} other {Загрузите файлы}}", + "torrents.details.general.comment": "Комментарий", + "torrents.details.general.connected": "{connected} подключено к {total}", + "torrents.details.general.date.added": "Добавлено", + "torrents.details.general.date.created": "Дата создания", + "torrents.details.general.downloaded": "Загружено", + "torrents.details.general.free.disk.space": "Свободное место на диске", + "torrents.details.general.hash": "Хэш", + "torrents.details.general.heading.general": "Общие положения", + "torrents.details.general.heading.torrent": "Торрент", + "torrents.details.general.heading.tracker": "Трекер", + "torrents.details.general.heading.transfer": "Перевод", + "torrents.details.general.location": "Местоположение", + "torrents.details.general.none": "Нет", + "torrents.details.general.peers": "Личеры", + "torrents.details.general.scheduler.ignored": "Игнорировать", + "torrents.details.general.scheduler.obeyed": "Пробежал", + "torrents.details.general.scheduler": "Планировщик", + "torrents.details.general.seeds": "Сиды", + "torrents.details.general.size": "Размер", + "torrents.details.general.tags": "Теги", + "torrents.details.general.tracker.message": "Сообщение трекера", + "torrents.details.general.type.private": "Приватный", + "torrents.details.general.type.public": "Публичный", + "torrents.details.general.type": "Тип", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Нет данных для этого торрента.", + "torrents.details.peers": "Личеры", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} Выбранный файл} other {{countElement} выбрал файлы}}", + "torrents.details.selected.files.set.priority": "Установить приоритет", + "torrents.details.trackers.no.data": "Для этого торрента нет данных трекера.", + "torrents.details.trackers.type": "Тип", + "torrents.details.trackers": "Трекеры", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Очистить фильтры", + "torrents.list.context.check.hash": "Проверить хэш", + "torrents.list.context.details": "Детали торрента", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Установить местоположение торрента", + "torrents.list.context.pause": "Пауза", + "torrents.list.context.download": "Скачать", + "torrents.list.context.priority": "Приоритет", + "torrents.list.context.remove": "Удалить", + "torrents.list.context.set.tags": "Установить теги", + "torrents.list.context.set.trackers": "Установить трекеры", + "torrents.list.context.start": "Начать", + "torrents.list.context.stop": "Остановить", + "torrents.list.no.torrents": "Нет торрентов для отображения.", + "torrents.list.drop": "Перетащите файлы сюда, чтобы добавить их.", + "torrents.list.cannot.connect": "Не удается подключиться к клиенту.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "из", + "torrents.move.button.set.location": "Установить местоположение", + "torrents.move.button.state.setting": "Настройка...", + "torrents.move.data.label": "Переместить данные", + "torrents.move.check_hash.label": "Проверять хэш", + "torrents.move.heading": "Установить местоположение торрента", + "torrents.properties.date.added": "Добавлено", + "torrents.properties.comment": "Комментарий", + "torrents.properties.creation.date": "Дата создания", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Скорость загрузки", + "torrents.properties.download.total": "Загружено", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Свободное место на диске", + "torrents.properties.hash": "Хэш", + "torrents.properties.ignore.schedule": "Игнорировать планировщик", + "torrents.properties.is.private": "Приватный", + "torrents.properties.name": "Наименование", + "torrents.properties.percentage": "Процент завершённых", + "torrents.properties.ratio": "Коэффициент", + "torrents.properties.size": "Размер файла", + "torrents.properties.tags": "Теги", + "torrents.properties.tracker.message": "Сообщение трекера", + "torrents.properties.upload.speed": "Скорость отдачи", + "torrents.properties.upload.total": "Загружено", + "torrents.properties.seeds": "Сиды", + "torrents.properties.peers": "Личеры", + "torrents.properties.trackers": "Трекеры", + "torrents.remove.are.you.sure": "Вы уверены, что хотите удалить {count, plural, =1 {# торрент} other {# торренты}}? ?", + "torrents.remove.delete.data": "Удалить данные", + "torrents.remove.error.no.torrents.selected": "Вы не выбрали ни одного торрента.", + "torrents.remove": "Удалить торренты", + "torrents.set.tags.button.set": "Установить теги", + "torrents.set.tags.heading": "Установить теги", + "torrents.set.tags.enter.tags": "Введите теги", + "torrents.set.trackers.button.set": "Установить трекеры", + "torrents.set.trackers.heading": "Установить трекеры", + "torrents.set.trackers.enter.tracker": "Введите трекер", + "torrents.set.trackers.loading.trackers": "Загрузка трекеров...", + "torrents.sort.title": "Сортировать по", + "connection-interruption.heading": "Не удается подключиться к клиенту", + "status.diskusage.title": "Использование диска", + "status.diskusage.used": "Использовано", + "status.diskusage.free": "Бесплатно", + "status.diskusage.total": "Итого", + "locale.language.auto": "Автоматически", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Уведомления", + "dependency.loading.torrent.taxonomy": "Таксономия торрента", + "dependency.loading.transfer.rate.details": "Данные по скорости передачи данных", + "dependency.loading.transfer.history": "История передачи данных", + "dependency.loading.torrent.list": "Список торрентов" +} diff --git a/client/src/javascript/i18n/translations/sr.json b/client/src/javascript/i18n/translations/sr.json new file mode 100644 index 000000000..27901cc9e --- /dev/null +++ b/client/src/javascript/i18n/translations/sr.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Add Torrent", + "actionbar.button.remove.torrent": "Remove Torrent", + "alert.torrent.add": "Successfully added {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Failed to add {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Successfully moved {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move.failed": "Failed to move {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Successfully removed {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Failed to remove {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Successfully saved settings.", + "auth.add.user": "Add User", + "auth.create.account": "Create Account", + "auth.create.an.account": "Create an account", + "auth.create.an.account.intro": "Welcome to Flood!", + "auth.current.user": "Current User", + "auth.error.username.empty": "Username cannot be empty.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "Log In", + "auth.login": "Login", + "auth.login.intro": "Log in to your account.", + "auth.password": "Password", + "auth.user.accounts": "User Accounts", + "auth.username": "Username", + "auth.admin": "Admin", + "auth.message.not.admin": "User is not Admin", + "button.add": "Add", + "button.cancel": "Cancel", + "button.close": "Close", + "button.download": "Download", + "button.no": "No", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Save Settings", + "button.save.feed": "Save", + "button.state.adding": "Adding...", + "button.yes": "Yes", + "button.new": "New", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Connection could not be verified.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Connectivity Issue", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "Add Download Rule", + "feeds.add.feed": "Add Feed", + "feeds.applicable.feed": "Applicable Feed", + "feeds.apply.tags": "Apply Tags", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Exclude Pattern", + "feeds.existing.feeds": "Existing Feeds", + "feeds.existing.rules": "Existing Rules", + "feeds.interval": "Interval", + "feeds.label": "Label", + "feeds.match.count": "{count, plural, =1 {# match} other {# matches}}", + "feeds.match.pattern": "Match Pattern", + "feeds.match": "Match", + "feeds.exclude": "Exclude", + "feeds.no.feeds.available": "No feeds available.", + "feeds.no.feeds.defined": "No feeds defined.", + "feeds.no.items.matching": "No items matching search term.", + "feeds.no.rules.defined": "No rules defined.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Select Feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start on load", + "feeds.tabs.download.rules": "Download Rules", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent Feeds", + "feeds.tags": "Tags", + "feeds.test.match": "Test Match Pattern", + "feeds.time.hr": "Hours", + "feeds.time.min": "Minutes", + "feeds.time.day": "Days", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Search term", + "feeds.search.term": "Search term", + "feeds.validation.invalid.regular.expression": "Invalid regular expression.", + "feeds.validation.must.select.feed": "You must select a feed.", + "feeds.validation.must.specify.destination": "You must specify a destination.", + "feeds.validation.must.specify.label": "You must specify a label.", + "feeds.validation.must.specify.valid.feed.url": "You must specify a valid feed URL.", + "feeds.validation.interval.not.positive": "The interval must be a positive integer.", + "feeds.browse.feeds": "Browse feeds", + "filesystem.empty.directory": "Empty directory.", + "filesystem.error.eacces": "Flood does not have permission to read this directory.", + "filesystem.error.enoent": "This path does not exist. It will be created.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "Fetching directory structure...", + "filesystem.parent.directory": "Parent Directory", + "filter.all": "All", + "filter.status.title": "Filter by Status", + "filter.status.downloading": "Downloading", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Complete", + "filter.status.active": "Active", + "filter.status.inactive": "Inactive", + "filter.status.error": "Error", + "filter.status.stopped": "Stopped", + "filter.status.checking": "Checking", + "filter.tracker.title": "Filter by Tracker", + "filter.tag.title": "Filter by Tag", + "filter.untagged": "Untagged", + "general.ago": "ago", + "general.at": "at", + "general.to": "to", + "general.of": "of", + "general.clipboard.copy": "Copy", + "general.clipboard.copied": "Copied", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.", + "mediainfo.fetching": "Fetching...", + "mediainfo.heading": "Mediainfo Output", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Finished Downloading", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Error Reported", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Clear All", + "notification.showing": "Showing", + "priority.dont.download": "Don't Download", + "priority.high": "High", + "priority.low": "Low", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Download Slots Global", + "settings.bandwidth.slots.download.label": "Download Slots Per Torrent", + "settings.bandwidth.slots.heading": "Slot Availability", + "settings.bandwidth.slots.upload.global.label": "Upload Slots Global", + "settings.bandwidth.slots.upload.label": "Upload Slots Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown Presets: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown Presets: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Global Download Rate Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Global Upload Rate Throttle", + "settings.bandwidth.transferrate.heading": "Transfer Rate Throttles", + "settings.connectivity.dht.label": "Enable DHT", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentralized Peer Discovery", + "settings.connectivity.incoming.heading": "Incoming Connections", + "settings.connectivity.ip.hostname.label": "Reported IP/Hostname", + "settings.connectivity.max.http.connections": "Maximum HTTP Connections", + "settings.connectivity.peer.exchange.label": "Enable Peer Exchange", + "settings.connectivity.peers.desired.label": "Peers Desired", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maximum Peers", + "settings.connectivity.peers.min.label": "Minimum Peers", + "settings.connectivity.peers.seeding.max.label": "Maximum Peers Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimum Peers Seeding", + "settings.connectivity.port.open.label": "Open Port", + "settings.connectivity.port.randomize.label": "Randomize Port", + "settings.connectivity.port.range.label": "Port Range", + "settings.resources.disk.check.hash.label": "Verify Hash on Completion", + "settings.resources.disk.download.location.label": "Default Download Directory", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maximum Open Files", + "settings.resources.memory.heading": "Memory", + "settings.resources.memory.max.label": "Max Memory Usage", + "settings.tabs.bandwidth": "Bandwidth", + "settings.tabs.connectivity": "Connectivity", + "settings.tabs.heading": "Settings", + "settings.tabs.resources": "Resources", + "settings.tabs.authentication": "Authentication", + "settings.tabs.userinterface": "User Interface", + "settings.tabs.diskusage": "Disk Usage", + "settings.tabs.about": "About", + "settings.ui.locale": "Locale", + "settings.ui.language": "Language", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent List Display", + "settings.ui.torrent.size": "Torrent Size", + "settings.ui.torrent.size.expanded": "Expanded View", + "settings.ui.torrent.size.condensed": "Condensed View", + "settings.ui.torrent.details.enabled": "Enabled", + "settings.ui.torrent.details.tags.placement": "In the expanded view, tags work best at the end of the list.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Show", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "About Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Settings", + "sidebar.button.speedlimits": "Speed Limits", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log Out", + "sidebar.search.placeholder": "Search torrents", + "sidebar.transferdata.downloaded": "Downloaded", + "sidebar.transferdata.uploaded": "Uploaded", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Unlimited", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "hr", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Add Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Destination", + "torrents.add.destination.placeholder": "Destination", + "torrents.add.heading": "Add Torrents", + "torrents.add.start.label": "Start Torrent", + "torrents.add.tab.file.browse": "or click to browse", + "torrents.add.tab.file.drop": "Drop some files here,", + "torrents.add.tab.file.title": "By File", + "torrents.add.tab.url.input.placeholder": "Torrent URL or Magnet Link", + "torrents.add.tab.url.title": "By URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "Use as Base Path", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Details", + "torrents.details.files": "Files", + "torrents.details.files.loading": "Loading file detail...", + "torrents.details.files.download.file": "{count, plural, =1 {Download File} other {Download Files}}", + "torrents.details.general.comment": "Comment", + "torrents.details.general.connected": "{connected} connected of {total}", + "torrents.details.general.date.added": "Added", + "torrents.details.general.date.created": "Creation Date", + "torrents.details.general.downloaded": "Downloaded", + "torrents.details.general.free.disk.space": "Free Disk Space", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "General", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfer", + "torrents.details.general.location": "Location", + "torrents.details.general.none": "None", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignored", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Scheduler", + "torrents.details.general.seeds": "Seeds", + "torrents.details.general.size": "Size", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Tracker Message", + "torrents.details.general.type.private": "Private", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "There is no peer data for this torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} selected file} other {{countElement} selected files}}", + "torrents.details.selected.files.set.priority": "Set Priority", + "torrents.details.trackers.no.data": "There is no tracker data for this torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Clear Filters", + "torrents.list.context.check.hash": "Check Hash", + "torrents.list.context.details": "Torrent Details", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Set Torrent Location", + "torrents.list.context.pause": "Pause", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "Priority", + "torrents.list.context.remove": "Remove", + "torrents.list.context.set.tags": "Set Tags", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stop", + "torrents.list.no.torrents": "No torrents to display.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "of", + "torrents.move.button.set.location": "Set Location", + "torrents.move.button.state.setting": "Setting...", + "torrents.move.data.label": "Move data", + "torrents.move.check_hash.label": "Check hash", + "torrents.move.heading": "Set Torrent Location", + "torrents.properties.date.added": "Added", + "torrents.properties.comment": "Comment", + "torrents.properties.creation.date": "Creation Date", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Download Speed", + "torrents.properties.download.total": "Downloaded", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Free Disk Space", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignore Scheduler", + "torrents.properties.is.private": "Private", + "torrents.properties.name": "Name", + "torrents.properties.percentage": "Percent Complete", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "File Size", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Tracker Message", + "torrents.properties.upload.speed": "Upload Speed", + "torrents.properties.upload.total": "Uploaded", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Are you sure you want to remove {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Delete data", + "torrents.remove.error.no.torrents.selected": "You haven't selected any torrents.", + "torrents.remove": "Remove Torrents", + "torrents.set.tags.button.set": "Set Tags", + "torrents.set.tags.heading": "Set Tags", + "torrents.set.tags.enter.tags": "Enter tags", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "Sort By", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Disk Usage", + "status.diskusage.used": "Used", + "status.diskusage.free": "Free", + "status.diskusage.total": "Total", + "locale.language.auto": "Automatic", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Details", + "dependency.loading.transfer.history": "Data Transfer History", + "dependency.loading.torrent.list": "Torrent List" +} diff --git a/client/src/javascript/i18n/translations/sv.json b/client/src/javascript/i18n/translations/sv.json new file mode 100644 index 000000000..8e38cfc15 --- /dev/null +++ b/client/src/javascript/i18n/translations/sv.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Starta Torrent", + "actionbar.button.stop.torrent": "Stoppa Torrent", + "actionbar.button.add.torrent": "Lägg till Torrent", + "actionbar.button.remove.torrent": "Ta bort Torrent", + "alert.torrent.add": "{countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Kunde inte lägga till {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Framgångsrikt flyttat {countElement} {count, plural, =1 {torrent} other {torrents}}", + "alert.torrent.move.failed": "Det gick inte att flytta {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Lyckades ta bort {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Det gick inte att ta bort {countElement} {count, plural, =1 {torrent} other {torrents}}", + "alert.settings.saved": "Inställningarna har sparats.", + "auth.add.user": "Lägg till användare", + "auth.create.account": "Skapa konto", + "auth.create.an.account": "Skapa ett konto", + "auth.create.an.account.intro": "Välkommen till översvämningen!", + "auth.current.user": "Nuvarande användare", + "auth.error.username.empty": "Användarnamnet får inte vara tomt.", + "auth.error.password.empty": "Lösenordet kan inte vara tomt.", + "auth.input.clear": "Clear", + "auth.log.in": "Logga in", + "auth.login": "Inloggning", + "auth.login.intro": "Logga in på ditt konto.", + "auth.password": "Lösenord", + "auth.user.accounts": "Användarkonton", + "auth.username": "Användarnamn", + "auth.admin": "Administratör", + "auth.message.not.admin": "Användaren är inte Admin", + "button.add": "Lägg till", + "button.cancel": "Avbryt", + "button.close": "Close", + "button.download": "Hämta", + "button.no": "Nej", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Spara inställningar", + "button.save.feed": "Spara", + "button.state.adding": "Lägger till...", + "button.yes": "Ja", + "button.new": "Ny", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Anslutningen kunde inte verifieras.", + "connection.settings.client.select": "Klient", + "connection.settings.error.empty": "Anslutningsinställningar kan inte vara tomt.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Typ av anslutning", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Värd", + "connection.settings.rtorrent.host.input.placeholder": "Värdnamn eller IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Sökväg", + "connection.settings.rtorrent.socket.input.placeholder": "Sökväg till uttag", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL till qBittorrent Web API", + "connection.settings.qbittorrent.username": "Användarnamn", + "connection.settings.qbittorrent.username.input.placeholder": "Användarnamn", + "connection.settings.qbittorrent.password": "Lösenord", + "connection.settings.qbittorrent.password.input.placeholder": "Lösenord", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Problem med anslutning", + "connectivity.modal.content": "Kan inte ansluta till klienten. Uppdatera anslutningsinställningarna.", + "feeds.add.automatic.download.rule": "Lägg till nedladdningsregel", + "feeds.add.feed": "Lägg till flöde", + "feeds.applicable.feed": "Tillämplig flöde", + "feeds.apply.tags": "Tillämpa taggar", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Exkludera mönster", + "feeds.existing.feeds": "Befintliga flöden", + "feeds.existing.rules": "Befintliga regler", + "feeds.interval": "Intervall", + "feeds.label": "Etikett", + "feeds.match.count": "{count, plural, =1 {# matcha} other {# matcha}}", + "feeds.match.pattern": "Matcha mönster", + "feeds.match": "Matcha", + "feeds.exclude": "Exkludera", + "feeds.no.feeds.available": "Inga flöden tillgängliga.", + "feeds.no.feeds.defined": "Inga flöden har definierats.", + "feeds.no.items.matching": "Inga objekt matchar söktermen.", + "feeds.no.rules.defined": "Inga regler definierade.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Välj flöde", + "feeds.select.interval": "Intervall", + "feeds.start.on.load": "Starta vid laddning", + "feeds.tabs.download.rules": "Ladda ner regler", + "feeds.tabs.feeds": "Flöden", + "feeds.tabs.heading": "Flöden för Torrent", + "feeds.tags": "Taggar", + "feeds.test.match": "Testa matchande mönster", + "feeds.time.hr": "Timmar", + "feeds.time.min": "Minuter", + "feeds.time.day": "Dagar", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Sök termin", + "feeds.search.term": "Sök termin", + "feeds.validation.invalid.regular.expression": "Ogiltigt reguljärt uttryck.", + "feeds.validation.must.select.feed": "Du måste välja ett flöde.", + "feeds.validation.must.specify.destination": "Du måste ange en destination.", + "feeds.validation.must.specify.label": "Du måste ange en etikett.", + "feeds.validation.must.specify.valid.feed.url": "Du måste ange en giltig flödes-URL.", + "feeds.validation.interval.not.positive": "Intervallet måste vara ett positivt heltal.", + "feeds.browse.feeds": "Bläddra bland flöden", + "filesystem.empty.directory": "Tom katalog.", + "filesystem.error.eacces": "Översvämningen har inte behörighet att läsa denna katalog.", + "filesystem.error.enoent": "Denna väg existerar inte. Den kommer att skapas.", + "filesystem.error.unknown": "Ett okänt fel inträffade. Försök igen.", + "filesystem.fetching": "Hämtar katalogstruktur...", + "filesystem.parent.directory": "Överordnad katalog", + "filter.all": "Alla", + "filter.status.title": "Filtrera efter status", + "filter.status.downloading": "Hämtar", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Complete", + "filter.status.active": "Aktiv", + "filter.status.inactive": "Inaktiv", + "filter.status.error": "Fel", + "filter.status.stopped": "Stoppad", + "filter.status.checking": "Kontrollerar", + "filter.tracker.title": "Filtrera efter Tracker", + "filter.tag.title": "Filtrera efter tagg", + "filter.untagged": "Otaggade", + "general.ago": "sedan", + "general.at": "vid", + "general.to": "till", + "general.of": "av", + "general.clipboard.copy": "Kopiera", + "general.clipboard.copied": "Kopierad", + "general.error.unknown": "Ett okänt fel uppstod", + "mediainfo.execError": "Ett fel uppstod när mediainfo kördes på servern. Kontrollera att mediainfo är installerat och tillgängligt i PATH till Flood.", + "mediainfo.fetching": "Hämtar...", + "mediainfo.heading": "Mediainfo utdata", + "notification.feed.torrent.added.heading": "Feed objekt köad", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Hämtning slutförd", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Fel rapporterat", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Rensa alla", + "notification.showing": "Visar", + "priority.dont.download": "Ladda inte ner", + "priority.high": "Hög", + "priority.low": "Låg", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Ladda ner Slots Global", + "settings.bandwidth.slots.download.label": "Ladda ner Slots per Torrent", + "settings.bandwidth.slots.heading": "Tillgänglighet för Slot", + "settings.bandwidth.slots.upload.global.label": "Ladda upp Slots Global", + "settings.bandwidth.slots.upload.label": "Ladda upp Slots Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown förinställningar: Ladda ner", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown förinställningar: Ladda upp", + "settings.bandwidth.transferrate.global.throttle.download": "Global nedladdningshastighet Trottel", + "settings.bandwidth.transferrate.global.throttle.upload": "Global uppladdningshastighet Trottel", + "settings.bandwidth.transferrate.heading": "Överföring hastighet trottoarer", + "settings.connectivity.dht.label": "Aktivera DHT", + "settings.connectivity.dht.port.label": "DHT port", + "settings.connectivity.dpd.heading": "Decentraliserad Peer Discovery", + "settings.connectivity.incoming.heading": "Inkommande anslutningar", + "settings.connectivity.ip.hostname.label": "Rapporterat IP/värdnamn", + "settings.connectivity.max.http.connections": "Maximal HTTP-anslutning", + "settings.connectivity.peer.exchange.label": "Aktivera Peer Exchange", + "settings.connectivity.peers.desired.label": "Jämlikar önskade", + "settings.connectivity.peers.heading": "Klienter", + "settings.connectivity.peers.max.label": "Maximalt antal klienter", + "settings.connectivity.peers.min.label": "Minsta antal klienter", + "settings.connectivity.peers.seeding.max.label": "Maximal klientfröning", + "settings.connectivity.peers.seeding.min.label": "Minsta antal klienter Seeding", + "settings.connectivity.port.open.label": "Öppna port", + "settings.connectivity.port.randomize.label": "Slumpa port", + "settings.connectivity.port.range.label": "Portens intervall", + "settings.resources.disk.check.hash.label": "Verifiera Hash vid slutförd", + "settings.resources.disk.download.location.label": "Förvald nedladdningsmapp", + "settings.resources.disk.heading": "Diskett", + "settings.resources.max.open.files": "Maximalt antal öppna filer", + "settings.resources.memory.heading": "Minne", + "settings.resources.memory.max.label": "Maximal minnesanvändning", + "settings.tabs.bandwidth": "Bandbredd", + "settings.tabs.connectivity": "Anslutning", + "settings.tabs.heading": "Inställningar", + "settings.tabs.resources": "Resurser", + "settings.tabs.authentication": "Autentisering", + "settings.tabs.userinterface": "Användargränssnitt", + "settings.tabs.diskusage": "Diskanvändning", + "settings.tabs.about": "Om", + "settings.ui.locale": "Lokalt", + "settings.ui.language": "Språk", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Visning av torrentlista", + "settings.ui.torrent.size": "Torrent Storlek", + "settings.ui.torrent.size.expanded": "Utökad vy", + "settings.ui.torrent.size.condensed": "Kondenserad vy", + "settings.ui.torrent.details.enabled": "Aktiverad", + "settings.ui.torrent.details.tags.placement": "I den utvidgade vyn fungerar taggar bäst i slutet av listan.", + "settings.ui.torrent.context.menu.items.show": "Visa", + "settings.ui.displayed.details": "Torrent Detaljkolumner", + "settings.ui.displayed.context.menu.items": "Kontext menyobjekt", + "settings.diskusage.show": "Visa", + "settings.diskusage.mount.points": "Disk användning monteringspunkter", + "settings.about.flood": "Om översvämning", + "sidebar.button.feeds": "Flöden", + "sidebar.button.settings": "Inställningar", + "sidebar.button.speedlimits": "Hastighetsgränser", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Logga ut", + "sidebar.search.placeholder": "Sök torrents", + "sidebar.transferdata.downloaded": "Nedladdad", + "sidebar.transferdata.uploaded": "Uppladdad", + "sidebar.speedlimits.download": "LADDA NER", + "sidebar.speedlimits.upload": "LADDA UPP", + "speed.unlimited": "Obegränsad", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "Tuberkulos", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "vk", + "unit.time.day": "d", + "unit.time.hour": "tim", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Lägg till Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Valfritt cookie-namn=cookie-värde", + "torrents.add.destination.label": "Mål", + "torrents.add.destination.placeholder": "Mål", + "torrents.add.heading": "Lägg till Torrents", + "torrents.add.start.label": "Starta Torrent", + "torrents.add.tab.file.browse": "eller klicka för att bläddra", + "torrents.add.tab.file.drop": "Släpp några filer här,", + "torrents.add.tab.file.title": "Efter fil", + "torrents.add.tab.url.input.placeholder": "Torrent URL eller Magnet länk", + "torrents.add.tab.url.title": "Efter URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Skapa", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Taggar", + "torrents.create.source.path.label": "Källa", + "torrents.create.trackers.label": "Spårare", + "torrents.create.tracker.input.placeholder": "Spårare URL", + "torrents.create.base.name.label": "Grundnamn", + "torrents.create.base.name.input.placeholder": "Valfri basfil eller katalognamn för torrent", + "torrents.create.comment.label": "Kommentar", + "torrents.create.comment.input.placeholder": "Valfri kommentar i torrent-fil", + "torrents.create.info.source.label": "Info Källa", + "torrents.create.info.source.input.placeholder": "Valfri källpost i infohash", + "torrents.create.is.private.label": "Privat", + "torrents.create.tags.input.placeholder": "Taggar i översvämning. Ej tillagd till skapad torrent.", + "torrents.destination.base_path": "Använd som grundsökväg", + "torrents.destination.completed": "Slutförd", + "torrents.details.actions.pause": "Pausa", + "torrents.details.actions.start": "Starta", + "torrents.details.actions.stop": "Stoppa", + "torrents.details.details": "Detaljer", + "torrents.details.files": "Filer", + "torrents.details.files.loading": "Laddar fildetalj...", + "torrents.details.files.download.file": "{count, plural, =1 {Ladda ner fil} other {Ladda ner filer}}", + "torrents.details.general.comment": "Kommentar", + "torrents.details.general.connected": "{connected} ansluten till {total}", + "torrents.details.general.date.added": "Tillagd", + "torrents.details.general.date.created": "Skapad datum", + "torrents.details.general.downloaded": "Nedladdad", + "torrents.details.general.free.disk.space": "Ledigt diskutrymme", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "Allmänt", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Spårare", + "torrents.details.general.heading.transfer": "Överföring", + "torrents.details.general.location": "Plats", + "torrents.details.general.none": "Ingen", + "torrents.details.general.peers": "Klienter", + "torrents.details.general.scheduler.ignored": "Ignorerad", + "torrents.details.general.scheduler.obeyed": "Lydd", + "torrents.details.general.scheduler": "Schemaläggare", + "torrents.details.general.seeds": "Frön", + "torrents.details.general.size": "Storlek", + "torrents.details.general.tags": "Taggar", + "torrents.details.general.tracker.message": "Spårare Meddelande", + "torrents.details.general.type.private": "Privat", + "torrents.details.general.type.public": "Publik", + "torrents.details.general.type": "Typ", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Det finns inga peer-data för denna torrent.", + "torrents.details.peers": "Klienter", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} vald fil} other {{countElement} valda filer}}", + "torrents.details.selected.files.set.priority": "Ange prioritet", + "torrents.details.trackers.no.data": "Det finns ingen spårningsdata för denna torrent.", + "torrents.details.trackers.type": "Typ", + "torrents.details.trackers": "Spårare", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Rensa filter", + "torrents.list.context.check.hash": "Kontrollera hasch", + "torrents.list.context.details": "Torrent Detaljer", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Ange torrentplats", + "torrents.list.context.pause": "Pausa", + "torrents.list.context.download": "Hämta", + "torrents.list.context.priority": "Prioritet", + "torrents.list.context.remove": "Radera", + "torrents.list.context.set.tags": "Ange taggar", + "torrents.list.context.set.trackers": "Ange spårare", + "torrents.list.context.start": "Starta", + "torrents.list.context.stop": "Stoppa", + "torrents.list.no.torrents": "Inga torrents att visa.", + "torrents.list.drop": "Släpp filer här för att lägga till dem.", + "torrents.list.cannot.connect": "Kan inte ansluta till klienten.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "av", + "torrents.move.button.set.location": "Ange plats", + "torrents.move.button.state.setting": "Inställer...", + "torrents.move.data.label": "Flytta data", + "torrents.move.check_hash.label": "Kontrollera hash", + "torrents.move.heading": "Ange torrentplats", + "torrents.properties.date.added": "Tillagd", + "torrents.properties.comment": "Kommentar", + "torrents.properties.creation.date": "Skapad datum", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Ladda ner hastighet", + "torrents.properties.download.total": "Nedladdad", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Ledigt diskutrymme", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignorera schemaläggare", + "torrents.properties.is.private": "Privat", + "torrents.properties.name": "Namn", + "torrents.properties.percentage": "Procent slutförd", + "torrents.properties.ratio": "Förhållande", + "torrents.properties.size": "Filstorlek", + "torrents.properties.tags": "Taggar", + "torrents.properties.tracker.message": "Spårare Meddelande", + "torrents.properties.upload.speed": "Ladda upp hastighet", + "torrents.properties.upload.total": "Uppladdad", + "torrents.properties.seeds": "Frön", + "torrents.properties.peers": "Klienter", + "torrents.properties.trackers": "Spårare", + "torrents.remove.are.you.sure": "Är du säker på att du vill ta bort {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Ta bort data", + "torrents.remove.error.no.torrents.selected": "Du har inte valt några torrenter.", + "torrents.remove": "Ta bort torrents", + "torrents.set.tags.button.set": "Ange taggar", + "torrents.set.tags.heading": "Ange taggar", + "torrents.set.tags.enter.tags": "Ange taggar", + "torrents.set.trackers.button.set": "Ange spårare", + "torrents.set.trackers.heading": "Ange spårare", + "torrents.set.trackers.enter.tracker": "Ange en tracker", + "torrents.set.trackers.loading.trackers": "Laddar spårare...", + "torrents.sort.title": "Sortera efter", + "connection-interruption.heading": "Kan inte ansluta till klienten", + "status.diskusage.title": "Diskanvändning", + "status.diskusage.used": "Använt", + "status.diskusage.free": "Gratis", + "status.diskusage.total": "Totalt", + "locale.language.auto": "Automatisk", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Aviseringar", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomi", + "dependency.loading.transfer.rate.details": "Detaljer för dataöverföringshastighet", + "dependency.loading.transfer.history": "Historik över dataöverföring", + "dependency.loading.torrent.list": "Torrent lista" +} diff --git a/client/src/javascript/i18n/translations/tr.json b/client/src/javascript/i18n/translations/tr.json new file mode 100644 index 000000000..27901cc9e --- /dev/null +++ b/client/src/javascript/i18n/translations/tr.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Add Torrent", + "actionbar.button.remove.torrent": "Remove Torrent", + "alert.torrent.add": "Successfully added {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Failed to add {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Successfully moved {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move.failed": "Failed to move {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Successfully removed {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Failed to remove {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Successfully saved settings.", + "auth.add.user": "Add User", + "auth.create.account": "Create Account", + "auth.create.an.account": "Create an account", + "auth.create.an.account.intro": "Welcome to Flood!", + "auth.current.user": "Current User", + "auth.error.username.empty": "Username cannot be empty.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "Log In", + "auth.login": "Login", + "auth.login.intro": "Log in to your account.", + "auth.password": "Password", + "auth.user.accounts": "User Accounts", + "auth.username": "Username", + "auth.admin": "Admin", + "auth.message.not.admin": "User is not Admin", + "button.add": "Add", + "button.cancel": "Cancel", + "button.close": "Close", + "button.download": "Download", + "button.no": "No", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Save Settings", + "button.save.feed": "Save", + "button.state.adding": "Adding...", + "button.yes": "Yes", + "button.new": "New", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Connection could not be verified.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Connectivity Issue", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "Add Download Rule", + "feeds.add.feed": "Add Feed", + "feeds.applicable.feed": "Applicable Feed", + "feeds.apply.tags": "Apply Tags", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Exclude Pattern", + "feeds.existing.feeds": "Existing Feeds", + "feeds.existing.rules": "Existing Rules", + "feeds.interval": "Interval", + "feeds.label": "Label", + "feeds.match.count": "{count, plural, =1 {# match} other {# matches}}", + "feeds.match.pattern": "Match Pattern", + "feeds.match": "Match", + "feeds.exclude": "Exclude", + "feeds.no.feeds.available": "No feeds available.", + "feeds.no.feeds.defined": "No feeds defined.", + "feeds.no.items.matching": "No items matching search term.", + "feeds.no.rules.defined": "No rules defined.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Select Feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start on load", + "feeds.tabs.download.rules": "Download Rules", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent Feeds", + "feeds.tags": "Tags", + "feeds.test.match": "Test Match Pattern", + "feeds.time.hr": "Hours", + "feeds.time.min": "Minutes", + "feeds.time.day": "Days", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Search term", + "feeds.search.term": "Search term", + "feeds.validation.invalid.regular.expression": "Invalid regular expression.", + "feeds.validation.must.select.feed": "You must select a feed.", + "feeds.validation.must.specify.destination": "You must specify a destination.", + "feeds.validation.must.specify.label": "You must specify a label.", + "feeds.validation.must.specify.valid.feed.url": "You must specify a valid feed URL.", + "feeds.validation.interval.not.positive": "The interval must be a positive integer.", + "feeds.browse.feeds": "Browse feeds", + "filesystem.empty.directory": "Empty directory.", + "filesystem.error.eacces": "Flood does not have permission to read this directory.", + "filesystem.error.enoent": "This path does not exist. It will be created.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "Fetching directory structure...", + "filesystem.parent.directory": "Parent Directory", + "filter.all": "All", + "filter.status.title": "Filter by Status", + "filter.status.downloading": "Downloading", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Complete", + "filter.status.active": "Active", + "filter.status.inactive": "Inactive", + "filter.status.error": "Error", + "filter.status.stopped": "Stopped", + "filter.status.checking": "Checking", + "filter.tracker.title": "Filter by Tracker", + "filter.tag.title": "Filter by Tag", + "filter.untagged": "Untagged", + "general.ago": "ago", + "general.at": "at", + "general.to": "to", + "general.of": "of", + "general.clipboard.copy": "Copy", + "general.clipboard.copied": "Copied", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.", + "mediainfo.fetching": "Fetching...", + "mediainfo.heading": "Mediainfo Output", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Finished Downloading", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Error Reported", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Clear All", + "notification.showing": "Showing", + "priority.dont.download": "Don't Download", + "priority.high": "High", + "priority.low": "Low", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Download Slots Global", + "settings.bandwidth.slots.download.label": "Download Slots Per Torrent", + "settings.bandwidth.slots.heading": "Slot Availability", + "settings.bandwidth.slots.upload.global.label": "Upload Slots Global", + "settings.bandwidth.slots.upload.label": "Upload Slots Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown Presets: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown Presets: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Global Download Rate Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Global Upload Rate Throttle", + "settings.bandwidth.transferrate.heading": "Transfer Rate Throttles", + "settings.connectivity.dht.label": "Enable DHT", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentralized Peer Discovery", + "settings.connectivity.incoming.heading": "Incoming Connections", + "settings.connectivity.ip.hostname.label": "Reported IP/Hostname", + "settings.connectivity.max.http.connections": "Maximum HTTP Connections", + "settings.connectivity.peer.exchange.label": "Enable Peer Exchange", + "settings.connectivity.peers.desired.label": "Peers Desired", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maximum Peers", + "settings.connectivity.peers.min.label": "Minimum Peers", + "settings.connectivity.peers.seeding.max.label": "Maximum Peers Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimum Peers Seeding", + "settings.connectivity.port.open.label": "Open Port", + "settings.connectivity.port.randomize.label": "Randomize Port", + "settings.connectivity.port.range.label": "Port Range", + "settings.resources.disk.check.hash.label": "Verify Hash on Completion", + "settings.resources.disk.download.location.label": "Default Download Directory", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maximum Open Files", + "settings.resources.memory.heading": "Memory", + "settings.resources.memory.max.label": "Max Memory Usage", + "settings.tabs.bandwidth": "Bandwidth", + "settings.tabs.connectivity": "Connectivity", + "settings.tabs.heading": "Settings", + "settings.tabs.resources": "Resources", + "settings.tabs.authentication": "Authentication", + "settings.tabs.userinterface": "User Interface", + "settings.tabs.diskusage": "Disk Usage", + "settings.tabs.about": "About", + "settings.ui.locale": "Locale", + "settings.ui.language": "Language", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent List Display", + "settings.ui.torrent.size": "Torrent Size", + "settings.ui.torrent.size.expanded": "Expanded View", + "settings.ui.torrent.size.condensed": "Condensed View", + "settings.ui.torrent.details.enabled": "Enabled", + "settings.ui.torrent.details.tags.placement": "In the expanded view, tags work best at the end of the list.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Show", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "About Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Settings", + "sidebar.button.speedlimits": "Speed Limits", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log Out", + "sidebar.search.placeholder": "Search torrents", + "sidebar.transferdata.downloaded": "Downloaded", + "sidebar.transferdata.uploaded": "Uploaded", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Unlimited", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "hr", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Add Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Destination", + "torrents.add.destination.placeholder": "Destination", + "torrents.add.heading": "Add Torrents", + "torrents.add.start.label": "Start Torrent", + "torrents.add.tab.file.browse": "or click to browse", + "torrents.add.tab.file.drop": "Drop some files here,", + "torrents.add.tab.file.title": "By File", + "torrents.add.tab.url.input.placeholder": "Torrent URL or Magnet Link", + "torrents.add.tab.url.title": "By URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "Use as Base Path", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Details", + "torrents.details.files": "Files", + "torrents.details.files.loading": "Loading file detail...", + "torrents.details.files.download.file": "{count, plural, =1 {Download File} other {Download Files}}", + "torrents.details.general.comment": "Comment", + "torrents.details.general.connected": "{connected} connected of {total}", + "torrents.details.general.date.added": "Added", + "torrents.details.general.date.created": "Creation Date", + "torrents.details.general.downloaded": "Downloaded", + "torrents.details.general.free.disk.space": "Free Disk Space", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "General", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfer", + "torrents.details.general.location": "Location", + "torrents.details.general.none": "None", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignored", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Scheduler", + "torrents.details.general.seeds": "Seeds", + "torrents.details.general.size": "Size", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Tracker Message", + "torrents.details.general.type.private": "Private", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "There is no peer data for this torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} selected file} other {{countElement} selected files}}", + "torrents.details.selected.files.set.priority": "Set Priority", + "torrents.details.trackers.no.data": "There is no tracker data for this torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Clear Filters", + "torrents.list.context.check.hash": "Check Hash", + "torrents.list.context.details": "Torrent Details", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Set Torrent Location", + "torrents.list.context.pause": "Pause", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "Priority", + "torrents.list.context.remove": "Remove", + "torrents.list.context.set.tags": "Set Tags", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stop", + "torrents.list.no.torrents": "No torrents to display.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "of", + "torrents.move.button.set.location": "Set Location", + "torrents.move.button.state.setting": "Setting...", + "torrents.move.data.label": "Move data", + "torrents.move.check_hash.label": "Check hash", + "torrents.move.heading": "Set Torrent Location", + "torrents.properties.date.added": "Added", + "torrents.properties.comment": "Comment", + "torrents.properties.creation.date": "Creation Date", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Download Speed", + "torrents.properties.download.total": "Downloaded", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Free Disk Space", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignore Scheduler", + "torrents.properties.is.private": "Private", + "torrents.properties.name": "Name", + "torrents.properties.percentage": "Percent Complete", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "File Size", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Tracker Message", + "torrents.properties.upload.speed": "Upload Speed", + "torrents.properties.upload.total": "Uploaded", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Are you sure you want to remove {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Delete data", + "torrents.remove.error.no.torrents.selected": "You haven't selected any torrents.", + "torrents.remove": "Remove Torrents", + "torrents.set.tags.button.set": "Set Tags", + "torrents.set.tags.heading": "Set Tags", + "torrents.set.tags.enter.tags": "Enter tags", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "Sort By", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Disk Usage", + "status.diskusage.used": "Used", + "status.diskusage.free": "Free", + "status.diskusage.total": "Total", + "locale.language.auto": "Automatic", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Details", + "dependency.loading.transfer.history": "Data Transfer History", + "dependency.loading.torrent.list": "Torrent List" +} diff --git a/client/src/javascript/i18n/translations/uk.json b/client/src/javascript/i18n/translations/uk.json new file mode 100644 index 000000000..e09360b97 --- /dev/null +++ b/client/src/javascript/i18n/translations/uk.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Почати торент", + "actionbar.button.stop.torrent": "Зупинити торрент", + "actionbar.button.add.torrent": "Додати Торрент", + "actionbar.button.remove.torrent": "Прибрати торент", + "alert.torrent.add": "Успішно додано {countElement} {count, plural, =1 {торрент} other {торрент}}.", + "alert.torrent.add.failed": "Не вдалося додати {countElement} {count, plural, =1 {торент} other {торрент}}.", + "alert.torrent.move": "Успішно переміщено {countElement} {count, plural, =1 {торент} other {торрент}}.", + "alert.torrent.move.failed": "Не вдалося перемістити {countElement} {count, plural, =1 {торент} other {торрент}}.", + "alert.torrent.remove": "Успішно видалено {countElement} {count, plural, =1 {торент} other {торрент}}.", + "alert.torrent.remove.failed": "Не вдалося видалити {countElement} {count, plural, =1 {торент} other {торрент}}.", + "alert.settings.saved": "Налаштування успішно збережені.", + "auth.add.user": "Добавити користувача", + "auth.create.account": "Створити обл. запис", + "auth.create.an.account": "Створити акаунт", + "auth.create.an.account.intro": "Ласкаво просимо до Флуту!", + "auth.current.user": "Поточний користувач", + "auth.error.username.empty": "Ім'я користувача не може бути порожнім.", + "auth.error.password.empty": "Пароль не може бути порожнім.", + "auth.input.clear": "Clear", + "auth.log.in": "Увійти", + "auth.login": "Логін", + "auth.login.intro": "Увійти до свого облікового запису.", + "auth.password": "Пароль", + "auth.user.accounts": "Облікові записи користувачів", + "auth.username": "Ім'я користувача", + "auth.admin": "Адмін", + "auth.message.not.admin": "Користувач не є адміністратором", + "button.add": "Додати", + "button.cancel": "Скасувати", + "button.close": "Close", + "button.download": "Звантажити", + "button.no": "Ні", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Зберегти Налаштування", + "button.save.feed": "Зберегти", + "button.state.adding": "Додаємо...", + "button.yes": "Так", + "button.new": "Новий", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Неможливо перевірити підключення.", + "connection.settings.client.select": "Клієнт", + "connection.settings.error.empty": "Параметри підключення не можуть бути порожніми.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Тип з'єднання", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Хост", + "connection.settings.rtorrent.host.input.placeholder": "Ім'я хоста або IP адреса", + "connection.settings.rtorrent.port": "Порт", + "connection.settings.rtorrent.port.input.placeholder": "Порт", + "connection.settings.rtorrent.socket": "Шлях", + "connection.settings.rtorrent.socket.input.placeholder": "Шлях до сокету", + "connection.settings.qbittorrent": "кБіторрент", + "connection.settings.qbittorrent.url": "Адреса", + "connection.settings.qbittorrent.url.input.placeholder": "URL на qBittorrent Web API", + "connection.settings.qbittorrent.username": "Ім'я користувача", + "connection.settings.qbittorrent.username.input.placeholder": "Ім'я користувача", + "connection.settings.qbittorrent.password": "Пароль", + "connection.settings.qbittorrent.password.input.placeholder": "Пароль", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Проблема з підключенням", + "connectivity.modal.content": "Не вдається підключитися до клієнта. Будь ласка, оновіть параметри підключення.", + "feeds.add.automatic.download.rule": "Додати правило завантаження", + "feeds.add.feed": "Добавити RSS Feed", + "feeds.applicable.feed": "Стрічка додатків", + "feeds.apply.tags": "Застосувати мітки", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Виключити ключ", + "feeds.existing.feeds": "Існуючі подачі", + "feeds.existing.rules": "Існуючі правила", + "feeds.interval": "Проміжок", + "feeds.label": "Мітка", + "feeds.match.count": "{count, plural, =1 {# співпадають} other {# матчів}}", + "feeds.match.pattern": "Шаблон матчу", + "feeds.match": "Матч", + "feeds.exclude": "Не включати", + "feeds.no.feeds.available": "Немає доступних каналів.", + "feeds.no.feeds.defined": "Каналів не визначено.", + "feeds.no.items.matching": "Немає елементів відповідних пошуковому запиту.", + "feeds.no.rules.defined": "Правила не визначені.", + "feeds.regEx": "Реджекс", + "feeds.select.feed": "Вибрати стрічку", + "feeds.select.interval": "Проміжок", + "feeds.start.on.load": "Запустити при завантаженні", + "feeds.tabs.download.rules": "Правила завантаження", + "feeds.tabs.feeds": "Стрічки новин", + "feeds.tabs.heading": "Торент фіди", + "feeds.tags": "Мітки", + "feeds.test.match": "Тест шаблону матчу", + "feeds.time.hr": "Годин", + "feeds.time.min": "Хвилин", + "feeds.time.day": "Днів", + "feeds.torrent.destination": "Призначення торенту", + "feeds.url": "Адреса", + "feeds.search": "Шукати за словом", + "feeds.search.term": "Шукати за словом", + "feeds.validation.invalid.regular.expression": "Недійсний регулярний вираз.", + "feeds.validation.must.select.feed": "Ви повинні вибрати канал.", + "feeds.validation.must.specify.destination": "Ви повинні вказати місце призначення.", + "feeds.validation.must.specify.label": "Будь ласка, вкажіть мітку.", + "feeds.validation.must.specify.valid.feed.url": "Необхідно вказати дійсний URL каналу.", + "feeds.validation.interval.not.positive": "Інтервал має бути додатним цілим числом.", + "feeds.browse.feeds": "Перегляд стрічок", + "filesystem.empty.directory": "Тека порожня.", + "filesystem.error.eacces": "Флуд не має дозволу на читання цієї теки.", + "filesystem.error.enoent": "Цей шлях не існує. Він буде створений.", + "filesystem.error.unknown": "Сталася невідома помилка, спробуйте ще раз.", + "filesystem.fetching": "Отримання структури каталогу...", + "filesystem.parent.directory": "Батьківський каталог", + "filter.all": "Всі", + "filter.status.title": "Фільтр за станом", + "filter.status.downloading": "Завантажується", + "filter.status.seeding": "Сідую", + "filter.status.completed": "Complete", + "filter.status.active": "Активний", + "filter.status.inactive": "Неактивний", + "filter.status.error": "Помилка", + "filter.status.stopped": "Зупинено", + "filter.status.checking": "Перевірка", + "filter.tracker.title": "Фільтр по трекеру", + "filter.tag.title": "Фільтр за ярликами", + "filter.untagged": "Без тегів", + "general.ago": "тому", + "general.at": "під", + "general.to": "по", + "general.of": "з", + "general.clipboard.copy": "Копія", + "general.clipboard.copied": "Скопійовано", + "general.error.unknown": "Сталася невідома помилка", + "mediainfo.execError": "Виникла помилка під час роботи mediainfo на сервері. Перевірте, що mediainfo встановлений і доступний у PATH to Flood.", + "mediainfo.fetching": "Отримання...", + "mediainfo.heading": "Вивід Середньовіччя", + "notification.feed.torrent.added.heading": "Черга RSS Feed'у", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Завершене завантаження", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Повідомлення про помилку", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Очистити все", + "notification.showing": "Показано", + "priority.dont.download": "Не завантажувати", + "priority.high": "Високий", + "priority.low": "Низька", + "priority.normal": "Нормальний", + "settings.bandwidth.slots.download.global.label": "Завантажити слоти глобальним", + "settings.bandwidth.slots.download.label": "Завантажити слоти на торент", + "settings.bandwidth.slots.heading": "Наявність слоту", + "settings.bandwidth.slots.upload.global.label": "Глобальна передача слотів", + "settings.bandwidth.slots.upload.label": "Відвантажити слоти за торент", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Пресети випадаючого списку: Завантажити", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Пресети випадаючого списку: Завантаження", + "settings.bandwidth.transferrate.global.throttle.download": "Збільшення швидкості завантаження", + "settings.bandwidth.transferrate.global.throttle.upload": "Збільшення швидкості вивантаження", + "settings.bandwidth.transferrate.heading": "Збільшення швидкості передачі", + "settings.connectivity.dht.label": "Увімкнути DHT", + "settings.connectivity.dht.port.label": "DHT порт", + "settings.connectivity.dpd.heading": "Децентралізований канал для учасника", + "settings.connectivity.incoming.heading": "Вхідні підключення", + "settings.connectivity.ip.hostname.label": "Повідомлено IP/Hostname", + "settings.connectivity.max.http.connections": "Максимальна HTTP підключень", + "settings.connectivity.peer.exchange.label": "Увімкнути обмін учасниками", + "settings.connectivity.peers.desired.label": "Учасників Бажано", + "settings.connectivity.peers.heading": "Учасники", + "settings.connectivity.peers.max.label": "Максимальна кількість учасників", + "settings.connectivity.peers.min.label": "Мінімум учасників", + "settings.connectivity.peers.seeding.max.label": "Максимальна кількість учасників роздачі", + "settings.connectivity.peers.seeding.min.label": "Мінімум учасників на роздачі", + "settings.connectivity.port.open.label": "Відкритий порт", + "settings.connectivity.port.randomize.label": "Перемішати порт", + "settings.connectivity.port.range.label": "Діапазон портів", + "settings.resources.disk.check.hash.label": "Перевірити хеш при завершенні", + "settings.resources.disk.download.location.label": "Тека для завантажень за замовчуванням", + "settings.resources.disk.heading": "Диск", + "settings.resources.max.open.files": "Максимум відкритих файлів", + "settings.resources.memory.heading": "Пам'ять", + "settings.resources.memory.max.label": "Макс. використання пам'яті", + "settings.tabs.bandwidth": "Ширина пропускної здатності", + "settings.tabs.connectivity": "Підключення", + "settings.tabs.heading": "Налаштування", + "settings.tabs.resources": "Ресурси", + "settings.tabs.authentication": "Автентифікація", + "settings.tabs.userinterface": "Інтерфейс користувача", + "settings.tabs.diskusage": "Використання диску", + "settings.tabs.about": "Про програму", + "settings.ui.locale": "Локалізація", + "settings.ui.language": "Мова:", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Відображення торрентів", + "settings.ui.torrent.size": "Розмір торенту", + "settings.ui.torrent.size.expanded": "Розширений вигляд", + "settings.ui.torrent.size.condensed": "Стиснутий вигляд", + "settings.ui.torrent.details.enabled": "Увімкнено", + "settings.ui.torrent.details.tags.placement": "У розширеному перегляді теги в кінці списку працюють найкраще.", + "settings.ui.torrent.context.menu.items.show": "Показати", + "settings.ui.displayed.details": "Стовпці подробиць торенту", + "settings.ui.displayed.context.menu.items": "Контекстне меню", + "settings.diskusage.show": "Показати", + "settings.diskusage.mount.points": "Очки для використання диску", + "settings.about.flood": "Про Flood", + "sidebar.button.feeds": "Стрічки новин", + "sidebar.button.settings": "Налаштування", + "sidebar.button.speedlimits": "Обмеження швидкості", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Вийти з системи", + "sidebar.search.placeholder": "Пошук торентів", + "sidebar.transferdata.downloaded": "Завантажено", + "sidebar.transferdata.uploaded": "Відвантажено", + "sidebar.speedlimits.download": "ЗАВАНТАЖИТИ", + "sidebar.speedlimits.upload": "ВИВАНТАЖИТИ", + "speed.unlimited": "Необмежено", + "unit.size.byte": "Сі", + "unit.size.kilobyte": "кБ", + "unit.size.megabyte": "МБ", + "unit.size.gigabyte": "ГБ", + "unit.size.terabyte": "ТБ", + "unit.speed": "{baseUnit}/с", + "unit.time.year": "yr", + "unit.time.week": "тижд", + "unit.time.day": "д", + "unit.time.hour": "год", + "unit.time.minute": "м", + "unit.time.second": "с", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Додати Торрент", + "torrents.add.cookies.label": "Файли Cookies", + "torrents.add.cookies.input.placeholder": "Необов'язкове значення cookie-name=cookie-value", + "torrents.add.destination.label": "Пункт призначення", + "torrents.add.destination.placeholder": "Пункт призначення", + "torrents.add.heading": "Додати торенти", + "torrents.add.start.label": "Почати торент", + "torrents.add.tab.file.browse": "або клацніть для перегляду", + "torrents.add.tab.file.drop": "Перетягніть сюди файли,", + "torrents.add.tab.file.title": "За файлом", + "torrents.add.tab.url.input.placeholder": "Адреса торенту або Magnet-посилання", + "torrents.add.tab.url.title": "За URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Створити", + "torrents.add.torrents.label": "Торенти", + "torrents.add.tags": "Мітки", + "torrents.create.source.path.label": "Джерело", + "torrents.create.trackers.label": "Трекери", + "torrents.create.tracker.input.placeholder": "URL трекера", + "torrents.create.base.name.label": "Базова назва", + "torrents.create.base.name.input.placeholder": "Необов'язковий файл або назва теки торента", + "torrents.create.comment.label": "Коментар", + "torrents.create.comment.input.placeholder": "Необов'язковий коментар у торент-файлі", + "torrents.create.info.source.label": "Інформація про джерело", + "torrents.create.info.source.input.placeholder": "Необов'язковий вихідний запис в infohash", + "torrents.create.is.private.label": "Приватний", + "torrents.create.tags.input.placeholder": "Теги в потоці. Не додано до створеного торенту.", + "torrents.destination.base_path": "Використовувати як Базовий шлях", + "torrents.destination.completed": "Завершені", + "torrents.details.actions.pause": "Пауза", + "torrents.details.actions.start": "Старт", + "torrents.details.actions.stop": "Зупинити", + "torrents.details.details": "Подробиці", + "torrents.details.files": "Файли", + "torrents.details.files.loading": "Завантаження подробиць файлу...", + "torrents.details.files.download.file": "{count, plural, =1 {Завантажити файл} other {Завантажити файл}}", + "torrents.details.general.comment": "Коментар", + "torrents.details.general.connected": "{connected} підключено до {total}", + "torrents.details.general.date.added": "Додано", + "torrents.details.general.date.created": "Дата створення", + "torrents.details.general.downloaded": "Завантажено", + "torrents.details.general.free.disk.space": "Вільний дисковий простір", + "torrents.details.general.hash": "Хеш", + "torrents.details.general.heading.general": "Загальні налаштування", + "torrents.details.general.heading.torrent": "Торент", + "torrents.details.general.heading.tracker": "Трекер", + "torrents.details.general.heading.transfer": "Переказ", + "torrents.details.general.location": "Місцезнаходження", + "torrents.details.general.none": "Без ефекту", + "torrents.details.general.peers": "Учасники", + "torrents.details.general.scheduler.ignored": "Ігнорується", + "torrents.details.general.scheduler.obeyed": "Підборіддя", + "torrents.details.general.scheduler": "Планувальник", + "torrents.details.general.seeds": "Насіння", + "torrents.details.general.size": "Розмір", + "torrents.details.general.tags": "Мітки", + "torrents.details.general.tracker.message": "Трекер повідомлення", + "torrents.details.general.type.private": "Приватний", + "torrents.details.general.type.public": "Загальнодоступна", + "torrents.details.general.type": "Тип", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "Немає даних для учасників цього торенту.", + "torrents.details.peers": "Учасники", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} Обраний файл} other {{countElement} обрані файли}}", + "torrents.details.selected.files.set.priority": "Встановити пріоритет", + "torrents.details.trackers.no.data": "Немає даних трекеру для цього торенту.", + "torrents.details.trackers.type": "Тип", + "torrents.details.trackers": "Трекери", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Скинути фільтри", + "torrents.list.context.check.hash": "Перевірити хеш", + "torrents.list.context.details": "Деталі торенту", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Встановити розташування торентів", + "torrents.list.context.pause": "Пауза", + "torrents.list.context.download": "Звантажити", + "torrents.list.context.priority": "Пріоритет", + "torrents.list.context.remove": "Видалити", + "torrents.list.context.set.tags": "Встановити позначки", + "torrents.list.context.set.trackers": "Встановити трекери", + "torrents.list.context.start": "Старт", + "torrents.list.context.stop": "Зупинити", + "torrents.list.no.torrents": "Немає торентів для відображення.", + "torrents.list.drop": "Перетягніть файли сюди, щоб додати їх.", + "torrents.list.cannot.connect": "Неможливо підключитися до клієнта.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "з", + "torrents.move.button.set.location": "Задати місцезнаходження", + "torrents.move.button.state.setting": "Налаштування...", + "torrents.move.data.label": "Переміщення даних", + "torrents.move.check_hash.label": "Перевірити хеш", + "torrents.move.heading": "Встановити розташування торентів", + "torrents.properties.date.added": "Додано", + "torrents.properties.comment": "Коментар", + "torrents.properties.creation.date": "Дата створення", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Швидкість завантаження", + "torrents.properties.download.total": "Завантажено", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Вільний дисковий простір", + "torrents.properties.hash": "Хеш", + "torrents.properties.ignore.schedule": "Ігнорувати планувальник", + "torrents.properties.is.private": "Приватний", + "torrents.properties.name": "Ім'я", + "torrents.properties.percentage": "Процент завершено", + "torrents.properties.ratio": "Співвідношення", + "torrents.properties.size": "Розмір файлу", + "torrents.properties.tags": "Мітки", + "torrents.properties.tracker.message": "Трекер повідомлення", + "torrents.properties.upload.speed": "Швидкість вивантаження", + "torrents.properties.upload.total": "Відвантажено", + "torrents.properties.seeds": "Насіння", + "torrents.properties.peers": "Учасники", + "torrents.properties.trackers": "Трекери", + "torrents.remove.are.you.sure": "Ви впевнені, що хочете видалити {count, plural, =1 {# торент} other {# торрентів}}?", + "torrents.remove.delete.data": "Видалити дані", + "torrents.remove.error.no.torrents.selected": "Ви не вибрали жодного торенту.", + "torrents.remove": "Вилучити торенти", + "torrents.set.tags.button.set": "Встановити позначки", + "torrents.set.tags.heading": "Встановити позначки", + "torrents.set.tags.enter.tags": "Введіть мітки", + "torrents.set.trackers.button.set": "Встановити трекери", + "torrents.set.trackers.heading": "Встановити трекери", + "torrents.set.trackers.enter.tracker": "Ввести трекер", + "torrents.set.trackers.loading.trackers": "Завантаження трекерів...", + "torrents.sort.title": "Сортувати за", + "connection-interruption.heading": "Не вдається підключитися до клієнта", + "status.diskusage.title": "Використання диску", + "status.diskusage.used": "Використано", + "status.diskusage.free": "Безкоштовно", + "status.diskusage.total": "Загальна сума замовлення", + "locale.language.auto": "Автоматично", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Сповіщення", + "dependency.loading.torrent.taxonomy": "Торент таксономії", + "dependency.loading.transfer.rate.details": "Деталі частоти передачі даних", + "dependency.loading.transfer.history": "Історія передавання даних", + "dependency.loading.torrent.list": "Торент-список" +} diff --git a/client/src/javascript/i18n/translations/vi.json b/client/src/javascript/i18n/translations/vi.json new file mode 100644 index 000000000..27901cc9e --- /dev/null +++ b/client/src/javascript/i18n/translations/vi.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "Start Torrent", + "actionbar.button.stop.torrent": "Stop Torrent", + "actionbar.button.add.torrent": "Add Torrent", + "actionbar.button.remove.torrent": "Remove Torrent", + "alert.torrent.add": "Successfully added {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.add.failed": "Failed to add {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move": "Successfully moved {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.move.failed": "Failed to move {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove": "Successfully removed {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.torrent.remove.failed": "Failed to remove {countElement} {count, plural, =1 {torrent} other {torrents}}.", + "alert.settings.saved": "Successfully saved settings.", + "auth.add.user": "Add User", + "auth.create.account": "Create Account", + "auth.create.an.account": "Create an account", + "auth.create.an.account.intro": "Welcome to Flood!", + "auth.current.user": "Current User", + "auth.error.username.empty": "Username cannot be empty.", + "auth.error.password.empty": "Password cannot be empty.", + "auth.input.clear": "Clear", + "auth.log.in": "Log In", + "auth.login": "Login", + "auth.login.intro": "Log in to your account.", + "auth.password": "Password", + "auth.user.accounts": "User Accounts", + "auth.username": "Username", + "auth.admin": "Admin", + "auth.message.not.admin": "User is not Admin", + "button.add": "Add", + "button.cancel": "Cancel", + "button.close": "Close", + "button.download": "Download", + "button.no": "No", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "Save Settings", + "button.save.feed": "Save", + "button.state.adding": "Adding...", + "button.yes": "Yes", + "button.new": "New", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "Connection could not be verified.", + "connection.settings.client.select": "Client", + "connection.settings.error.empty": "Connection settings can not be empty.", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "Connection Type", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "Host", + "connection.settings.rtorrent.host.input.placeholder": "Hostname or IP", + "connection.settings.rtorrent.port": "Port", + "connection.settings.rtorrent.port.input.placeholder": "Port", + "connection.settings.rtorrent.socket": "Path", + "connection.settings.rtorrent.socket.input.placeholder": "Path to socket", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "URL", + "connection.settings.qbittorrent.url.input.placeholder": "URL to qBittorrent Web API", + "connection.settings.qbittorrent.username": "Username", + "connection.settings.qbittorrent.username.input.placeholder": "Username", + "connection.settings.qbittorrent.password": "Password", + "connection.settings.qbittorrent.password.input.placeholder": "Password", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "Connectivity Issue", + "connectivity.modal.content": "Cannot connect to the client. Please update connection settings.", + "feeds.add.automatic.download.rule": "Add Download Rule", + "feeds.add.feed": "Add Feed", + "feeds.applicable.feed": "Applicable Feed", + "feeds.apply.tags": "Apply Tags", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "Exclude Pattern", + "feeds.existing.feeds": "Existing Feeds", + "feeds.existing.rules": "Existing Rules", + "feeds.interval": "Interval", + "feeds.label": "Label", + "feeds.match.count": "{count, plural, =1 {# match} other {# matches}}", + "feeds.match.pattern": "Match Pattern", + "feeds.match": "Match", + "feeds.exclude": "Exclude", + "feeds.no.feeds.available": "No feeds available.", + "feeds.no.feeds.defined": "No feeds defined.", + "feeds.no.items.matching": "No items matching search term.", + "feeds.no.rules.defined": "No rules defined.", + "feeds.regEx": "RegEx", + "feeds.select.feed": "Select Feed", + "feeds.select.interval": "Interval", + "feeds.start.on.load": "Start on load", + "feeds.tabs.download.rules": "Download Rules", + "feeds.tabs.feeds": "Feeds", + "feeds.tabs.heading": "Torrent Feeds", + "feeds.tags": "Tags", + "feeds.test.match": "Test Match Pattern", + "feeds.time.hr": "Hours", + "feeds.time.min": "Minutes", + "feeds.time.day": "Days", + "feeds.torrent.destination": "Torrent Destination", + "feeds.url": "URL", + "feeds.search": "Search term", + "feeds.search.term": "Search term", + "feeds.validation.invalid.regular.expression": "Invalid regular expression.", + "feeds.validation.must.select.feed": "You must select a feed.", + "feeds.validation.must.specify.destination": "You must specify a destination.", + "feeds.validation.must.specify.label": "You must specify a label.", + "feeds.validation.must.specify.valid.feed.url": "You must specify a valid feed URL.", + "feeds.validation.interval.not.positive": "The interval must be a positive integer.", + "feeds.browse.feeds": "Browse feeds", + "filesystem.empty.directory": "Empty directory.", + "filesystem.error.eacces": "Flood does not have permission to read this directory.", + "filesystem.error.enoent": "This path does not exist. It will be created.", + "filesystem.error.unknown": "An unknown error occurred. Please try again.", + "filesystem.fetching": "Fetching directory structure...", + "filesystem.parent.directory": "Parent Directory", + "filter.all": "All", + "filter.status.title": "Filter by Status", + "filter.status.downloading": "Downloading", + "filter.status.seeding": "Seeding", + "filter.status.completed": "Complete", + "filter.status.active": "Active", + "filter.status.inactive": "Inactive", + "filter.status.error": "Error", + "filter.status.stopped": "Stopped", + "filter.status.checking": "Checking", + "filter.tracker.title": "Filter by Tracker", + "filter.tag.title": "Filter by Tag", + "filter.untagged": "Untagged", + "general.ago": "ago", + "general.at": "at", + "general.to": "to", + "general.of": "of", + "general.clipboard.copy": "Copy", + "general.clipboard.copied": "Copied", + "general.error.unknown": "An unknown error occurred", + "mediainfo.execError": "An error occurred while running mediainfo on the server. Check that mediainfo is installed and available in the PATH to Flood.", + "mediainfo.fetching": "Fetching...", + "mediainfo.heading": "Mediainfo Output", + "notification.feed.torrent.added.heading": "Feed Item Queued", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "Finished Downloading", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "Error Reported", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "Clear All", + "notification.showing": "Showing", + "priority.dont.download": "Don't Download", + "priority.high": "High", + "priority.low": "Low", + "priority.normal": "Normal", + "settings.bandwidth.slots.download.global.label": "Download Slots Global", + "settings.bandwidth.slots.download.label": "Download Slots Per Torrent", + "settings.bandwidth.slots.heading": "Slot Availability", + "settings.bandwidth.slots.upload.global.label": "Upload Slots Global", + "settings.bandwidth.slots.upload.label": "Upload Slots Per Torrent", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "Dropdown Presets: Download", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "Dropdown Presets: Upload", + "settings.bandwidth.transferrate.global.throttle.download": "Global Download Rate Throttle", + "settings.bandwidth.transferrate.global.throttle.upload": "Global Upload Rate Throttle", + "settings.bandwidth.transferrate.heading": "Transfer Rate Throttles", + "settings.connectivity.dht.label": "Enable DHT", + "settings.connectivity.dht.port.label": "DHT Port", + "settings.connectivity.dpd.heading": "Decentralized Peer Discovery", + "settings.connectivity.incoming.heading": "Incoming Connections", + "settings.connectivity.ip.hostname.label": "Reported IP/Hostname", + "settings.connectivity.max.http.connections": "Maximum HTTP Connections", + "settings.connectivity.peer.exchange.label": "Enable Peer Exchange", + "settings.connectivity.peers.desired.label": "Peers Desired", + "settings.connectivity.peers.heading": "Peers", + "settings.connectivity.peers.max.label": "Maximum Peers", + "settings.connectivity.peers.min.label": "Minimum Peers", + "settings.connectivity.peers.seeding.max.label": "Maximum Peers Seeding", + "settings.connectivity.peers.seeding.min.label": "Minimum Peers Seeding", + "settings.connectivity.port.open.label": "Open Port", + "settings.connectivity.port.randomize.label": "Randomize Port", + "settings.connectivity.port.range.label": "Port Range", + "settings.resources.disk.check.hash.label": "Verify Hash on Completion", + "settings.resources.disk.download.location.label": "Default Download Directory", + "settings.resources.disk.heading": "Disk", + "settings.resources.max.open.files": "Maximum Open Files", + "settings.resources.memory.heading": "Memory", + "settings.resources.memory.max.label": "Max Memory Usage", + "settings.tabs.bandwidth": "Bandwidth", + "settings.tabs.connectivity": "Connectivity", + "settings.tabs.heading": "Settings", + "settings.tabs.resources": "Resources", + "settings.tabs.authentication": "Authentication", + "settings.tabs.userinterface": "User Interface", + "settings.tabs.diskusage": "Disk Usage", + "settings.tabs.about": "About", + "settings.ui.locale": "Locale", + "settings.ui.language": "Language", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "Torrent List Display", + "settings.ui.torrent.size": "Torrent Size", + "settings.ui.torrent.size.expanded": "Expanded View", + "settings.ui.torrent.size.condensed": "Condensed View", + "settings.ui.torrent.details.enabled": "Enabled", + "settings.ui.torrent.details.tags.placement": "In the expanded view, tags work best at the end of the list.", + "settings.ui.torrent.context.menu.items.show": "Show", + "settings.ui.displayed.details": "Torrent Detail Columns", + "settings.ui.displayed.context.menu.items": "Context Menu Items", + "settings.diskusage.show": "Show", + "settings.diskusage.mount.points": "Disk Usage Mount Points", + "settings.about.flood": "About Flood", + "sidebar.button.feeds": "Feeds", + "sidebar.button.settings": "Settings", + "sidebar.button.speedlimits": "Speed Limits", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "Log Out", + "sidebar.search.placeholder": "Search torrents", + "sidebar.transferdata.downloaded": "Downloaded", + "sidebar.transferdata.uploaded": "Uploaded", + "sidebar.speedlimits.download": "DOWNLOAD", + "sidebar.speedlimits.upload": "UPLOAD", + "speed.unlimited": "Unlimited", + "unit.size.byte": "B", + "unit.size.kilobyte": "kB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/s", + "unit.time.year": "yr", + "unit.time.week": "wk", + "unit.time.day": "d", + "unit.time.hour": "hr", + "unit.time.minute": "m", + "unit.time.second": "s", + "unit.time.infinity": "∞", + "torrents.add.button.add": "Add Torrent", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "Optional cookie-name=cookie-value", + "torrents.add.destination.label": "Destination", + "torrents.add.destination.placeholder": "Destination", + "torrents.add.heading": "Add Torrents", + "torrents.add.start.label": "Start Torrent", + "torrents.add.tab.file.browse": "or click to browse", + "torrents.add.tab.file.drop": "Drop some files here,", + "torrents.add.tab.file.title": "By File", + "torrents.add.tab.url.input.placeholder": "Torrent URL or Magnet Link", + "torrents.add.tab.url.title": "By URL", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "Create", + "torrents.add.torrents.label": "Torrents", + "torrents.add.tags": "Tags", + "torrents.create.source.path.label": "Source", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker URL", + "torrents.create.base.name.label": "Base Name", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "Use as Base Path", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "Pause", + "torrents.details.actions.start": "Start", + "torrents.details.actions.stop": "Stop", + "torrents.details.details": "Details", + "torrents.details.files": "Files", + "torrents.details.files.loading": "Loading file detail...", + "torrents.details.files.download.file": "{count, plural, =1 {Download File} other {Download Files}}", + "torrents.details.general.comment": "Comment", + "torrents.details.general.connected": "{connected} connected of {total}", + "torrents.details.general.date.added": "Added", + "torrents.details.general.date.created": "Creation Date", + "torrents.details.general.downloaded": "Downloaded", + "torrents.details.general.free.disk.space": "Free Disk Space", + "torrents.details.general.hash": "Hash", + "torrents.details.general.heading.general": "General", + "torrents.details.general.heading.torrent": "Torrent", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "Transfer", + "torrents.details.general.location": "Location", + "torrents.details.general.none": "None", + "torrents.details.general.peers": "Peers", + "torrents.details.general.scheduler.ignored": "Ignored", + "torrents.details.general.scheduler.obeyed": "Obeyed", + "torrents.details.general.scheduler": "Scheduler", + "torrents.details.general.seeds": "Seeds", + "torrents.details.general.size": "Size", + "torrents.details.general.tags": "Tags", + "torrents.details.general.tracker.message": "Tracker Message", + "torrents.details.general.type.private": "Private", + "torrents.details.general.type.public": "Public", + "torrents.details.general.type": "Type", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "There is no peer data for this torrent.", + "torrents.details.peers": "Peers", + "torrents.details.selected.files": "{count, plural, =1 {{countElement} selected file} other {{countElement} selected files}}", + "torrents.details.selected.files.set.priority": "Set Priority", + "torrents.details.trackers.no.data": "There is no tracker data for this torrent.", + "torrents.details.trackers.type": "Type", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "Clear Filters", + "torrents.list.context.check.hash": "Check Hash", + "torrents.list.context.details": "Torrent Details", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "Set Torrent Location", + "torrents.list.context.pause": "Pause", + "torrents.list.context.download": "Download", + "torrents.list.context.priority": "Priority", + "torrents.list.context.remove": "Remove", + "torrents.list.context.set.tags": "Set Tags", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "Start", + "torrents.list.context.stop": "Stop", + "torrents.list.no.torrents": "No torrents to display.", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} {of} {total}", + "torrent.list.peers.of": "of", + "torrents.move.button.set.location": "Set Location", + "torrents.move.button.state.setting": "Setting...", + "torrents.move.data.label": "Move data", + "torrents.move.check_hash.label": "Check hash", + "torrents.move.heading": "Set Torrent Location", + "torrents.properties.date.added": "Added", + "torrents.properties.comment": "Comment", + "torrents.properties.creation.date": "Creation Date", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "Download Speed", + "torrents.properties.download.total": "Downloaded", + "torrents.properties.eta": "ETA", + "torrents.properties.free.disk.space": "Free Disk Space", + "torrents.properties.hash": "Hash", + "torrents.properties.ignore.schedule": "Ignore Scheduler", + "torrents.properties.is.private": "Private", + "torrents.properties.name": "Name", + "torrents.properties.percentage": "Percent Complete", + "torrents.properties.ratio": "Ratio", + "torrents.properties.size": "File Size", + "torrents.properties.tags": "Tags", + "torrents.properties.tracker.message": "Tracker Message", + "torrents.properties.upload.speed": "Upload Speed", + "torrents.properties.upload.total": "Uploaded", + "torrents.properties.seeds": "Seeds", + "torrents.properties.peers": "Peers", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "Are you sure you want to remove {count, plural, =1 {# torrent} other {# torrents}}?", + "torrents.remove.delete.data": "Delete data", + "torrents.remove.error.no.torrents.selected": "You haven't selected any torrents.", + "torrents.remove": "Remove Torrents", + "torrents.set.tags.button.set": "Set Tags", + "torrents.set.tags.heading": "Set Tags", + "torrents.set.tags.enter.tags": "Enter tags", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "Sort By", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "Disk Usage", + "status.diskusage.used": "Used", + "status.diskusage.free": "Free", + "status.diskusage.total": "Total", + "locale.language.auto": "Automatic", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "Notifications", + "dependency.loading.torrent.taxonomy": "Torrent Taxonomy", + "dependency.loading.transfer.rate.details": "Data Transfer Rate Details", + "dependency.loading.transfer.history": "Data Transfer History", + "dependency.loading.torrent.list": "Torrent List" +} diff --git a/client/src/javascript/i18n/translations/zh-Hans.json b/client/src/javascript/i18n/translations/zh-Hans.json new file mode 100644 index 000000000..a329dcba1 --- /dev/null +++ b/client/src/javascript/i18n/translations/zh-Hans.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "启动种子", + "actionbar.button.stop.torrent": "停止种子", + "actionbar.button.add.torrent": "添加种子", + "actionbar.button.remove.torrent": "移除种子", + "alert.torrent.add": "成功添加 {countElement} 个种子。", + "alert.torrent.add.failed": "{countElement} 个种子添加失败。", + "alert.torrent.move": "移动 {countElement} 个种子成功。", + "alert.torrent.move.failed": "{countElement} 个种子移动失败。", + "alert.torrent.remove": "成功移除 {countElement} 个种子。", + "alert.torrent.remove.failed": "{countElement} 个种子移除失败。", + "alert.settings.saved": "设置保存成功!", + "auth.add.user": "增加用户", + "auth.create.account": "创建账号", + "auth.create.an.account": "创建一个账号", + "auth.create.an.account.intro": "欢迎使用 Flood!", + "auth.current.user": "当前用户", + "auth.error.username.empty": "用户名不能为空", + "auth.error.password.empty": "密码不能为空。", + "auth.input.clear": "Clear", + "auth.log.in": "登录", + "auth.login": "登录", + "auth.login.intro": "登录您的帐户。", + "auth.password": "密码", + "auth.user.accounts": "用户账号", + "auth.username": "用户名", + "auth.admin": "管理员", + "auth.message.not.admin": "用户不是管理员", + "button.add": "新增", + "button.cancel": "取消", + "button.close": "Close", + "button.download": "下载", + "button.no": "取消", + "button.ok": "OK", + "button.retry": "Retry", + "button.save": "保存设置", + "button.save.feed": "保存", + "button.state.adding": "正在添加...", + "button.yes": "是", + "button.new": "添加", + "connection-interruption.action.selection.retry": "Retry with current client connection settings", + "connection-interruption.action.selection.config": "Update client connection settings", + "connection-interruption.not.admin": "Please contact your Flood administrator if this continues.", + "connection-interruption.verification-error": "无法建立连接。", + "connection.settings.client.select": "客户端:", + "connection.settings.error.empty": "连接设置不能为空。", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "连接类型", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "Exposing rTorrent via TCP may allow privilege escalation.", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "主机", + "connection.settings.rtorrent.host.input.placeholder": "主机名或IP", + "connection.settings.rtorrent.port": "端口", + "connection.settings.rtorrent.port.input.placeholder": "端口", + "connection.settings.rtorrent.socket": "路径", + "connection.settings.rtorrent.socket.input.placeholder": "套接字路径", + "connection.settings.qbittorrent": "qBitTorrent", + "connection.settings.qbittorrent.url": "网址", + "connection.settings.qbittorrent.url.input.placeholder": "qBittorrent Web API 的 URL", + "connection.settings.qbittorrent.username": "用户名", + "connection.settings.qbittorrent.username.input.placeholder": "用户名", + "connection.settings.qbittorrent.password": "密码", + "connection.settings.qbittorrent.password.input.placeholder": "密码", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "URL", + "connection.settings.transmission.url.input.placeholder": "URL to Transmission RPC interface", + "connection.settings.transmission.username": "Username", + "connection.settings.transmission.username.input.placeholder": "Username", + "connection.settings.transmission.password": "Password", + "connection.settings.transmission.password.input.placeholder": "Password", + "connectivity.modal.title": "连接失败", + "connectivity.modal.content": "无法连接到客户端。请更新连接设置。", + "feeds.add.automatic.download.rule": "增加下载规则", + "feeds.add.feed": "添加订阅源", + "feeds.applicable.feed": "可用的订阅源", + "feeds.apply.tags": "变更标签", + "feeds.check": "Validate the rule by trying it out. Not saved or sent.", + "feeds.exclude.pattern": "排除规则", + "feeds.existing.feeds": "现有的订阅源", + "feeds.existing.rules": "现有的规则", + "feeds.interval": "间隔", + "feeds.label": "标签", + "feeds.match.count": "# 个匹配", + "feeds.match.pattern": "匹配规则", + "feeds.match": "匹配", + "feeds.exclude": "排除", + "feeds.no.feeds.available": "没有可用的订阅源。", + "feeds.no.feeds.defined": "没有定义订阅源。", + "feeds.no.items.matching": "没有匹配搜索词的项目。", + "feeds.no.rules.defined": "没有定义规则。", + "feeds.regEx": "正则", + "feeds.select.feed": "选择订阅源", + "feeds.select.interval": "间隔", + "feeds.start.on.load": "读取后开始", + "feeds.tabs.download.rules": "下载规则", + "feeds.tabs.feeds": "订阅源", + "feeds.tabs.heading": "种子源", + "feeds.tags": "标签", + "feeds.test.match": "测试匹配模式", + "feeds.time.hr": "时", + "feeds.time.min": "分", + "feeds.time.day": "天", + "feeds.torrent.destination": "下载位置", + "feeds.url": "网址", + "feeds.search": "搜索关键词", + "feeds.search.term": "搜索词", + "feeds.validation.invalid.regular.expression": "无效的RegEx。", + "feeds.validation.must.select.feed": "你必须选择一个订阅源。", + "feeds.validation.must.specify.destination": "你必须指定下载位置。", + "feeds.validation.must.specify.label": "你必须指定标签。", + "feeds.validation.must.specify.valid.feed.url": "你必须指定有效的订阅源网址。", + "feeds.validation.interval.not.positive": "间隔必须是一个正整数。", + "feeds.browse.feeds": "浏览订阅源", + "filesystem.empty.directory": "空目录。", + "filesystem.error.eacces": "Flood没有权限读取此目录。", + "filesystem.error.enoent": "路径不存在,将会创建此目录。", + "filesystem.error.unknown": "发生未知错误。请重试。", + "filesystem.fetching": "获取目录结构...", + "filesystem.parent.directory": "父目录", + "filter.all": "全部", + "filter.status.title": "按状态筛选", + "filter.status.downloading": "下载中", + "filter.status.seeding": "做种中", + "filter.status.completed": "已完成", + "filter.status.active": "活动中", + "filter.status.inactive": "非活动中", + "filter.status.error": "错误", + "filter.status.stopped": "已停止", + "filter.status.checking": "校验中", + "filter.tracker.title": "按Tracker筛选", + "filter.tag.title": "按标签筛选", + "filter.untagged": "无标签", + "general.ago": "前", + "general.at": "在", + "general.to": "到", + "general.of": "的", + "general.clipboard.copy": "复制", + "general.clipboard.copied": "复制成功", + "general.error.unknown": "发生未知错误", + "mediainfo.execError": "在服务器上运行mediainfo时发生错误。检查mediainfo是否已经安装,并且在PATH中!", + "mediainfo.fetching": "获取中...", + "mediainfo.heading": "Mediainfo 输出", + "notification.feed.torrent.added.heading": "新闻源排队了", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "下载完成!", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "错误报告", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "全部清除", + "notification.showing": "显示", + "priority.dont.download": "不下载", + "priority.high": "高", + "priority.low": "低", + "priority.normal": "正常", + "settings.bandwidth.slots.download.global.label": "全局下载槽数", + "settings.bandwidth.slots.download.label": "每个种子下载槽数", + "settings.bandwidth.slots.heading": "传输槽位限制", + "settings.bandwidth.slots.upload.global.label": "全局上传槽数", + "settings.bandwidth.slots.upload.label": "每个种子上传槽数", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "下载速度限制菜单", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "上传速度限制菜单", + "settings.bandwidth.transferrate.global.throttle.download": "全局下载速度限制", + "settings.bandwidth.transferrate.global.throttle.upload": "全局上传速度限制", + "settings.bandwidth.transferrate.heading": "传输速度限制", + "settings.connectivity.dht.label": "开启 DHT", + "settings.connectivity.dht.port.label": "DHT 端口", + "settings.connectivity.dpd.heading": "分散式节点发现", + "settings.connectivity.incoming.heading": "传入连接", + "settings.connectivity.ip.hostname.label": "上报 IP/主机名", + "settings.connectivity.max.http.connections": "最大HTTP连接数", + "settings.connectivity.peer.exchange.label": "启用节点交换", + "settings.connectivity.peers.desired.label": "期望节点数", + "settings.connectivity.peers.heading": "节点", + "settings.connectivity.peers.max.label": "最大节点数", + "settings.connectivity.peers.min.label": "最小节点数", + "settings.connectivity.peers.seeding.max.label": "最大做种数", + "settings.connectivity.peers.seeding.min.label": "最小做种数", + "settings.connectivity.port.open.label": "开放端口", + "settings.connectivity.port.randomize.label": "随机端口", + "settings.connectivity.port.range.label": "端口范围", + "settings.resources.disk.check.hash.label": "完成后校验", + "settings.resources.disk.download.location.label": "默认下载目录", + "settings.resources.disk.heading": "磁盘", + "settings.resources.max.open.files": "最大打开文件数", + "settings.resources.memory.heading": "内存", + "settings.resources.memory.max.label": "最大内存使用", + "settings.tabs.bandwidth": "带宽", + "settings.tabs.connectivity": "连接", + "settings.tabs.heading": "设置", + "settings.tabs.resources": "资源", + "settings.tabs.authentication": "认证", + "settings.tabs.userinterface": "用户接口", + "settings.tabs.diskusage": "磁盘使用", + "settings.tabs.about": "关于", + "settings.ui.locale": "本地化", + "settings.ui.language": "语言", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "Single Selection", + "settings.ui.tag.selector.mode.multi": "Multi Selection", + "settings.ui.torrent.list": "种子列表显示模式", + "settings.ui.torrent.size": "种子大小", + "settings.ui.torrent.size.expanded": "展开模式", + "settings.ui.torrent.size.condensed": "紧凑模式", + "settings.ui.torrent.details.enabled": "启用", + "settings.ui.torrent.details.tags.placement": "在展开模式中,标签最好能够在列表末尾。", + "settings.ui.torrent.context.menu.items.show": "显示", + "settings.ui.displayed.details": "种子详情列表", + "settings.ui.displayed.context.menu.items": "上下文菜单项", + "settings.diskusage.show": "显示", + "settings.diskusage.mount.points": "磁盘挂载点", + "settings.about.flood": "关于Flood", + "sidebar.button.feeds": "订阅源", + "sidebar.button.settings": "设置", + "sidebar.button.speedlimits": "速度限制", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "登出", + "sidebar.search.placeholder": "搜索种子", + "sidebar.transferdata.downloaded": "已下载", + "sidebar.transferdata.uploaded": "已上传", + "sidebar.speedlimits.download": "下载", + "sidebar.speedlimits.upload": "上传", + "speed.unlimited": "无限制", + "unit.size.byte": "B", + "unit.size.kilobyte": "千B", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/秒", + "unit.time.year": "年", + "unit.time.week": "周", + "unit.time.day": "天", + "unit.time.hour": "时", + "unit.time.minute": "分", + "unit.time.second": "秒", + "unit.time.infinity": "∞", + "torrents.add.button.add": "添加种子", + "torrents.add.cookies.label": "Cookie", + "torrents.add.cookies.input.placeholder": "可选的 cookie-name=cookie值", + "torrents.add.destination.label": "下载位置", + "torrents.add.destination.placeholder": "下载位置", + "torrents.add.heading": "添加种子", + "torrents.add.start.label": "开始下载", + "torrents.add.tab.file.browse": "或点击浏览", + "torrents.add.tab.file.drop": "将文件拖至此处,", + "torrents.add.tab.file.title": "上传文件", + "torrents.add.tab.url.input.placeholder": "种子网址或磁力链接", + "torrents.add.tab.url.title": "从网址", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "创建", + "torrents.add.torrents.label": "种子", + "torrents.add.tags": "标签", + "torrents.create.source.path.label": "来源", + "torrents.create.trackers.label": "追踪器", + "torrents.create.tracker.input.placeholder": "追踪器 URL", + "torrents.create.base.name.label": "基本名称", + "torrents.create.base.name.input.placeholder": "种子的可选基础文件或目录名称", + "torrents.create.comment.label": "评论", + "torrents.create.comment.input.placeholder": "种子文件中的可选评论", + "torrents.create.info.source.label": "信息来源", + "torrents.create.info.source.input.placeholder": "信息哈希中的可选源项", + "torrents.create.is.private.label": "非公开的", + "torrents.create.tags.input.placeholder": "Flood中的标签。没有添加到创建种子。", + "torrents.destination.base_path": "作为基础路径", + "torrents.destination.completed": "已完成", + "torrents.details.actions.pause": "暂停", + "torrents.details.actions.start": "开始", + "torrents.details.actions.stop": "停止", + "torrents.details.details": "详情", + "torrents.details.files": "文件", + "torrents.details.files.loading": "正在加载详细信息...", + "torrents.details.files.download.file": "下载文件", + "torrents.details.general.comment": "评论", + "torrents.details.general.connected": "已连接到 {connected} / {total}", + "torrents.details.general.date.added": "添加时间", + "torrents.details.general.date.created": "创建时间", + "torrents.details.general.downloaded": "已下载", + "torrents.details.general.free.disk.space": "可用磁盘空间", + "torrents.details.general.hash": "哈希", + "torrents.details.general.heading.general": "概况", + "torrents.details.general.heading.torrent": "种子", + "torrents.details.general.heading.tracker": "追踪器", + "torrents.details.general.heading.transfer": "传输", + "torrents.details.general.location": "位置", + "torrents.details.general.none": "无", + "torrents.details.general.peers": "节点", + "torrents.details.general.scheduler.ignored": "已忽略", + "torrents.details.general.scheduler.obeyed": "已遵守", + "torrents.details.general.scheduler": "调度器", + "torrents.details.general.seeds": "做种者", + "torrents.details.general.size": "大小", + "torrents.details.general.tags": "标签", + "torrents.details.general.tracker.message": "Tracker 信息", + "torrents.details.general.type.private": "私有", + "torrents.details.general.type.public": "公开", + "torrents.details.general.type": "类型", + "torrents.details.mediainfo": "Mediainfo", + "torrents.details.peers.no.data": "这个种子没有节点信息。", + "torrents.details.peers": "节点", + "torrents.details.selected.files": "已选择 {countElement} 个文件", + "torrents.details.selected.files.set.priority": "设置优先级", + "torrents.details.trackers.no.data": "这个种子没有 Tracker 信息。", + "torrents.details.trackers.type": "类型", + "torrents.details.trackers": "Tracker 服务器", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "清除筛选", + "torrents.list.context.check.hash": "校验", + "torrents.list.context.details": "种子详情", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "设置种子位置", + "torrents.list.context.pause": "暂停", + "torrents.list.context.download": "下载", + "torrents.list.context.priority": "优先级", + "torrents.list.context.remove": "移除", + "torrents.list.context.set.tags": "设置标签", + "torrents.list.context.set.trackers": "设置追踪器", + "torrents.list.context.start": "开始", + "torrents.list.context.stop": "停止", + "torrents.list.no.torrents": "没有可显示的种子。", + "torrents.list.drop": "将文件拖放到此处以添加它们。", + "torrents.list.cannot.connect": "无法连接到客户端。", + "torrent.list.peers": "{connected} / {total}", + "torrent.list.peers.of": "/", + "torrents.move.button.set.location": "设置路径", + "torrents.move.button.state.setting": "设置中...", + "torrents.move.data.label": "移动数据", + "torrents.move.check_hash.label": "校验", + "torrents.move.heading": "设置种子路径", + "torrents.properties.date.added": "添加时间", + "torrents.properties.comment": "备注", + "torrents.properties.creation.date": "创建时间", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "下载速度", + "torrents.properties.download.total": "已下载", + "torrents.properties.eta": "剩余时间", + "torrents.properties.free.disk.space": "可用磁盘空间", + "torrents.properties.hash": "哈希", + "torrents.properties.ignore.schedule": "忽略调度器", + "torrents.properties.is.private": "私有", + "torrents.properties.name": "名称", + "torrents.properties.percentage": "完成百分比", + "torrents.properties.ratio": "分享率", + "torrents.properties.size": "文件大小", + "torrents.properties.tags": "标签", + "torrents.properties.tracker.message": "Tracker 信息", + "torrents.properties.upload.speed": "上传速度", + "torrents.properties.upload.total": "已上传", + "torrents.properties.seeds": "种子", + "torrents.properties.peers": "对等点", + "torrents.properties.trackers": "追踪器", + "torrents.remove.are.you.sure": "你确定你想要删除 # 个种子", + "torrents.remove.delete.data": "删除数据", + "torrents.remove.error.no.torrents.selected": "您没有选择任何种子。", + "torrents.remove": "移除种子", + "torrents.set.tags.button.set": "设置标签", + "torrents.set.tags.heading": "设置标签", + "torrents.set.tags.enter.tags": "输入标签", + "torrents.set.trackers.button.set": "设置追踪器", + "torrents.set.trackers.heading": "设置追踪器", + "torrents.set.trackers.enter.tracker": "输入追踪器", + "torrents.set.trackers.loading.trackers": "正在加载追踪器...", + "torrents.sort.title": "排序方式", + "connection-interruption.heading": "无法连接到客户端", + "status.diskusage.title": "磁盘使用量", + "status.diskusage.used": "已使用", + "status.diskusage.free": "免费的", + "status.diskusage.total": "总计", + "locale.language.auto": "自动", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "通知", + "dependency.loading.torrent.taxonomy": "种子分类", + "dependency.loading.transfer.rate.details": "数据传输速率详细信息", + "dependency.loading.transfer.history": "数据传输历史", + "dependency.loading.torrent.list": "种子列表" +} diff --git a/client/src/javascript/i18n/translations/zh-Hant.json b/client/src/javascript/i18n/translations/zh-Hant.json new file mode 100644 index 000000000..26073931d --- /dev/null +++ b/client/src/javascript/i18n/translations/zh-Hant.json @@ -0,0 +1,381 @@ +{ + "actionbar.button.start.torrent": "開始下載", + "actionbar.button.stop.torrent": "停止下載", + "actionbar.button.add.torrent": "新增種子", + "actionbar.button.remove.torrent": "移除種子", + "alert.torrent.add": "成功新增 {countElement} 個種子。", + "alert.torrent.add.failed": "新增 {countElement} 個種子失敗。", + "alert.torrent.move": "成功移動 {countElement} 個種子。", + "alert.torrent.move.failed": "移動 {countElement} 個種子失敗。", + "alert.torrent.remove": "成功移除 {countElement} 個種子。", + "alert.torrent.remove.failed": "移除 {countElement} 個種子失敗。", + "alert.settings.saved": "設定儲存成功。", + "auth.add.user": "新增使用者", + "auth.create.account": "建立帳號", + "auth.create.an.account": "建立一個帳號", + "auth.create.an.account.intro": "歡迎使用 Flood!", + "auth.current.user": "目前使用者", + "auth.error.username.empty": "使用者名稱不可以留空!", + "auth.error.password.empty": "密碼不能留空。", + "auth.input.clear": "清除", + "auth.log.in": "登入", + "auth.login": "登入", + "auth.login.intro": "登入你的帳號", + "auth.password": "密碼", + "auth.user.accounts": "使用者帳號", + "auth.username": "使用者名稱", + "auth.admin": "管理員", + "auth.message.not.admin": "使用者不是管理員", + "button.add": "新增", + "button.cancel": "取消", + "button.close": "Close", + "button.download": "下載", + "button.no": "否", + "button.ok": "確定", + "button.retry": "重試", + "button.save": "儲存設定", + "button.save.feed": "儲存", + "button.state.adding": "新增中...", + "button.yes": "是", + "button.new": "新增", + "connection-interruption.action.selection.retry": "用目前的連線設定重試", + "connection-interruption.action.selection.config": "更新連線設定", + "connection-interruption.not.admin": "如果這個錯誤持續發生,請聯繫Flood的管理員。", + "connection-interruption.verification-error": "無法建立連線。", + "connection.settings.client.select": "用戶端", + "connection.settings.error.empty": "連線設定不可以是空白的。", + "connection.settings.rtorrent": "rTorrent", + "connection.settings.rtorrent.type": "連線類型", + "connection.settings.rtorrent.type.tcp": "TCP", + "connection.settings.rtorrent.type.tcp.warning": "透過TCP曝露rTorrent可能會會有特權提升問題。", + "connection.settings.rtorrent.type.socket": "Socket", + "connection.settings.rtorrent.host": "主機", + "connection.settings.rtorrent.host.input.placeholder": "主機名稱或IP", + "connection.settings.rtorrent.port": "連接埠", + "connection.settings.rtorrent.port.input.placeholder": "連接埠", + "connection.settings.rtorrent.socket": "路徑", + "connection.settings.rtorrent.socket.input.placeholder": "Socket路徑", + "connection.settings.qbittorrent": "qBittorrent", + "connection.settings.qbittorrent.url": "網址", + "connection.settings.qbittorrent.url.input.placeholder": "qBittorrent Web API 網址", + "connection.settings.qbittorrent.username": "使用者名稱", + "connection.settings.qbittorrent.username.input.placeholder": "使用者名稱", + "connection.settings.qbittorrent.password": "密碼", + "connection.settings.qbittorrent.password.input.placeholder": "密碼", + "connection.settings.transmission": "Transmission", + "connection.settings.transmission.url": "網址", + "connection.settings.transmission.url.input.placeholder": "Transmission RPC 介面網址", + "connection.settings.transmission.username": "使用者名稱", + "connection.settings.transmission.username.input.placeholder": "使用者名稱", + "connection.settings.transmission.password": "密碼", + "connection.settings.transmission.password.input.placeholder": "密碼", + "connectivity.modal.title": "連線問題", + "connectivity.modal.content": "無法連線到用戶端,請更新您的連線設定。", + "feeds.add.automatic.download.rule": "新增下載規則", + "feeds.add.feed": "新增訊息來源", + "feeds.applicable.feed": "可使用的訊息來源", + "feeds.apply.tags": "套用標籤", + "feeds.check": "此為規則驗證,不會保存或送出。", + "feeds.exclude.pattern": "例外Pattern", + "feeds.existing.feeds": "已存在的訊息來源", + "feeds.existing.rules": "已存在的規則 ", + "feeds.interval": "內部", + "feeds.label": "標籤", + "feeds.match.count": "{count, plural, =1 {# 項符合} other {# 項符合}}", + "feeds.match.pattern": "符合Pattern", + "feeds.match": "符合", + "feeds.exclude": "除外", + "feeds.no.feeds.available": "沒有可用的訊息來源", + "feeds.no.feeds.defined": "沒有任何訊息來源", + "feeds.no.items.matching": "沒有符合搜尋條件的項目", + "feeds.no.rules.defined": "沒有任何規則。", + "feeds.regEx": "正則表達式", + "feeds.select.feed": "選擇訊息來源", + "feeds.select.interval": "內部", + "feeds.start.on.load": "讀取後開始", + "feeds.tabs.download.rules": "下載規則", + "feeds.tabs.feeds": "訊息來源(Feeds)", + "feeds.tabs.heading": "種子訊息來源(Feeds)", + "feeds.tags": "標籤", + "feeds.test.match": "測試Pattern", + "feeds.time.hr": "小時", + "feeds.time.min": "分鐘", + "feeds.time.day": "日", + "feeds.torrent.destination": "下載位置", + "feeds.url": "網址", + "feeds.search": "搜尋關鍵字", + "feeds.search.term": "搜尋條件", + "feeds.validation.invalid.regular.expression": "無效的正則表達式。", + "feeds.validation.must.select.feed": "你必須選擇一個訊息來源。", + "feeds.validation.must.specify.destination": "您必須指定下載位置。", + "feeds.validation.must.specify.label": "你必須指定標籤。", + "feeds.validation.must.specify.valid.feed.url": "你必須指定一個有效的訊息來源網址。", + "feeds.validation.interval.not.positive": "警告限制必須是正整數", + "feeds.browse.feeds": "瀏覽訊息來源", + "filesystem.empty.directory": "空白資料夾。", + "filesystem.error.eacces": "Flood 沒有權限去讀取資料夾的內容。", + "filesystem.error.enoent": "路徑不存在,系統會自動建立。", + "filesystem.error.unknown": "發生未知錯誤,請再試一次。", + "filesystem.fetching": "抓取資料夾結構中...", + "filesystem.parent.directory": "上一層目錄", + "filter.all": "全部", + "filter.status.title": "狀態篩選", + "filter.status.downloading": "下載中", + "filter.status.seeding": "做種中", + "filter.status.completed": "已完成", + "filter.status.active": "活躍的", + "filter.status.inactive": "不活躍的", + "filter.status.error": "錯誤", + "filter.status.stopped": "已停止", + "filter.status.checking": "檢查中", + "filter.tracker.title": "Tracker篩選", + "filter.tag.title": "標籤篩選", + "filter.untagged": "未標籤", + "general.ago": "前", + "general.at": "於", + "general.to": "給", + "general.of": "的", + "general.clipboard.copy": "複製", + "general.clipboard.copied": "已複製", + "general.error.unknown": "發生未知的錯誤", + "mediainfo.execError": "執行 MediaInfo 發生錯誤,請確認系統中有安裝 mediainfo 且該執行檔位於 PATH 變數中。", + "mediainfo.fetching": "抓取中...", + "mediainfo.heading": "Mediainfo輸出", + "notification.feed.torrent.added.heading": "已排入佇列", + "notification.feed.torrent.added.body": "{title}", + "notification.no.notification": "No notification to display.", + "notification.torrent.finished.heading": "下載完成", + "notification.torrent.finished.body": "{name}", + "notification.torrent.errored.heading": "錯誤回報", + "notification.torrent.errored.body": "{name}", + "notification.clear.all": "全部清除", + "notification.showing": "檢視", + "priority.dont.download": "不要下載", + "priority.high": "高優先權", + "priority.low": "低優先權", + "priority.normal": "中優先權", + "settings.bandwidth.slots.download.global.label": "最大同時下載數", + "settings.bandwidth.slots.download.label": "每個 Torrent 最大下載數", + "settings.bandwidth.slots.heading": "傳輸個數限制", + "settings.bandwidth.slots.upload.global.label": "最大同時上傳數", + "settings.bandwidth.slots.upload.label": "每個 Torrent 最大上傳數", + "settings.bandwidth.transferrate.dropdown.preset.download.label": "下拉選單可選擇下載速度", + "settings.bandwidth.transferrate.dropdown.preset.upload.label": "下拉選單可選擇上傳速度", + "settings.bandwidth.transferrate.global.throttle.download": "總下載速度限制", + "settings.bandwidth.transferrate.global.throttle.upload": "總上傳速度限制", + "settings.bandwidth.transferrate.heading": "傳輸速度限制", + "settings.connectivity.dht.label": "啟用 DHT", + "settings.connectivity.dht.port.label": "DHT 連接埠", + "settings.connectivity.dpd.heading": "分散式節點發現", + "settings.connectivity.incoming.heading": "連入連線", + "settings.connectivity.ip.hostname.label": "回報IP/主機名稱", + "settings.connectivity.max.http.connections": "最大 HTTP 連線", + "settings.connectivity.peer.exchange.label": "啟用節點交換", + "settings.connectivity.peers.desired.label": "需要節點數", + "settings.connectivity.peers.heading": "節點", + "settings.connectivity.peers.max.label": "最大節點數", + "settings.connectivity.peers.min.label": "最小節點數", + "settings.connectivity.peers.seeding.max.label": "最大作種節點數", + "settings.connectivity.peers.seeding.min.label": "最小作種節點數", + "settings.connectivity.port.open.label": "開放連接埠", + "settings.connectivity.port.randomize.label": "隨機連接埠", + "settings.connectivity.port.range.label": "連接埠範圍", + "settings.resources.disk.check.hash.label": "完成時驗證雜湊", + "settings.resources.disk.download.location.label": "預設下載資料夾", + "settings.resources.disk.heading": "磁碟", + "settings.resources.max.open.files": "最大開啟檔案數", + "settings.resources.memory.heading": "記憶體", + "settings.resources.memory.max.label": "最大記憶體使用量", + "settings.tabs.bandwidth": "頻寬", + "settings.tabs.connectivity": "連線", + "settings.tabs.heading": "設定", + "settings.tabs.resources": "資源", + "settings.tabs.authentication": "驗證", + "settings.tabs.userinterface": "使用者介面", + "settings.tabs.diskusage": "磁碟使用量", + "settings.tabs.about": "關於", + "settings.ui.locale": "語系", + "settings.ui.language": "語言", + "settings.ui.tag.selector.mode": "Tag Selector Preference", + "settings.ui.tag.selector.mode.single": "單選", + "settings.ui.tag.selector.mode.multi": "複選", + "settings.ui.torrent.list": "種子列表顯示模式", + "settings.ui.torrent.size": "種子大小", + "settings.ui.torrent.size.expanded": "展開模式", + "settings.ui.torrent.size.condensed": "壓縮模式", + "settings.ui.torrent.details.enabled": "啟用", + "settings.ui.torrent.details.tags.placement": "在展開模式中,把標籤放在列表的最後一項會比較好。", + "settings.ui.torrent.context.menu.items.show": "顯示", + "settings.ui.displayed.details": "種子詳細資訊欄位", + "settings.ui.displayed.context.menu.items": "右鍵選單項目", + "settings.diskusage.show": "顯示", + "settings.diskusage.mount.points": "掛載節點磁碟空間使用量", + "settings.about.flood": "關於Flood", + "sidebar.button.feeds": "訊息來源", + "sidebar.button.settings": "設定", + "sidebar.button.speedlimits": "速度限制", + "sidebar.button.theme.dark": "Dark Theme", + "sidebar.button.theme.light": "Light Theme", + "sidebar.button.log.out": "登出", + "sidebar.search.placeholder": "搜尋種子", + "sidebar.transferdata.downloaded": "已下載", + "sidebar.transferdata.uploaded": "已上傳", + "sidebar.speedlimits.download": "下載速度", + "sidebar.speedlimits.upload": "上傳速度", + "speed.unlimited": "無限制", + "unit.size.byte": "B", + "unit.size.kilobyte": "KB", + "unit.size.megabyte": "MB", + "unit.size.gigabyte": "GB", + "unit.size.terabyte": "TB", + "unit.speed": "{baseUnit}/秒", + "unit.time.year": "年", + "unit.time.week": "週", + "unit.time.day": "天", + "unit.time.hour": "時", + "unit.time.minute": "分", + "unit.time.second": "秒", + "unit.time.infinity": "∞", + "torrents.add.button.add": "新增種子", + "torrents.add.cookies.label": "Cookies", + "torrents.add.cookies.input.placeholder": "(選填) cookie-name=cookie-value", + "torrents.add.destination.label": "下載位置", + "torrents.add.destination.placeholder": "下載位置", + "torrents.add.heading": "新增種子", + "torrents.add.start.label": "新增後開始下載", + "torrents.add.tab.file.browse": "或點擊瀏覽", + "torrents.add.tab.file.drop": "拖曳檔案到這個視窗", + "torrents.add.tab.file.title": "從檔案", + "torrents.add.tab.url.input.placeholder": "種子網址或磁力連結", + "torrents.add.tab.url.title": "從網址", + "torrents.add.tab.url.register.magnet.handler": "Register to handle magnet links", + "torrents.add.tab.create.title": "建立", + "torrents.add.torrents.label": "種子", + "torrents.add.tags": "標籤", + "torrents.create.source.path.label": "來源", + "torrents.create.trackers.label": "Trackers", + "torrents.create.tracker.input.placeholder": "Tracker 網址", + "torrents.create.base.name.label": "名稱", + "torrents.create.base.name.input.placeholder": "Optional base file or directory name of the torrent", + "torrents.create.comment.label": "Comment", + "torrents.create.comment.input.placeholder": "Optional comment in torrent file", + "torrents.create.info.source.label": "Info Source", + "torrents.create.info.source.input.placeholder": "Optional source entry in infohash", + "torrents.create.is.private.label": "Private", + "torrents.create.tags.input.placeholder": "Tags in Flood. Not added to created torrent.", + "torrents.destination.base_path": "當作基本路徑", + "torrents.destination.completed": "Completed", + "torrents.details.actions.pause": "暫停", + "torrents.details.actions.start": "開始", + "torrents.details.actions.stop": "停止", + "torrents.details.details": "詳細資訊", + "torrents.details.files": "檔案", + "torrents.details.files.loading": "讀取檔案詳細資訊...", + "torrents.details.files.download.file": "下載檔案", + "torrents.details.general.comment": "評論", + "torrents.details.general.connected": "已連線到 {total} 中的 {connected}", + "torrents.details.general.date.added": "新增日期", + "torrents.details.general.date.created": "建立日期", + "torrents.details.general.downloaded": "已下載", + "torrents.details.general.free.disk.space": "可用磁碟空間", + "torrents.details.general.hash": "雜湊值", + "torrents.details.general.heading.general": "通用", + "torrents.details.general.heading.torrent": "種子", + "torrents.details.general.heading.tracker": "Tracker", + "torrents.details.general.heading.transfer": "傳輸", + "torrents.details.general.location": "位置", + "torrents.details.general.none": "無", + "torrents.details.general.peers": "節點", + "torrents.details.general.scheduler.ignored": "忽略", + "torrents.details.general.scheduler.obeyed": "直接下載", + "torrents.details.general.scheduler": "排程", + "torrents.details.general.seeds": "節點使用者", + "torrents.details.general.size": "大小", + "torrents.details.general.tags": "標籤", + "torrents.details.general.tracker.message": "Tracker訊息", + "torrents.details.general.type.private": "私有", + "torrents.details.general.type.public": "公開", + "torrents.details.general.type": "類型", + "torrents.details.mediainfo": "MediaInfo", + "torrents.details.peers.no.data": "這個種子沒有任何節點資訊。", + "torrents.details.peers": "節點", + "torrents.details.selected.files": "已選擇 {countElement} 個檔案", + "torrents.details.selected.files.set.priority": "設定優先權", + "torrents.details.trackers.no.data": "這個種子沒有任何 tracker 資訊。", + "torrents.details.trackers.type": "類型", + "torrents.details.trackers": "Trackers", + "torrents.generate.magnet.heading": "Generate Magnet Link", + "torrents.generate.magnet.loading.trackers": "Loading trackers...", + "torrents.generate.magnet.private.torrent": "This is a private torrent.", + "torrents.generate.magnet.magnet": "Magnet Link", + "torrents.generate.magnet.magnet.with.trackers": "Magnet Link with Trackers", + "torrents.list.clear.filters": "清除篩選條件", + "torrents.list.context.check.hash": "檢查雜湊", + "torrents.list.context.details": "種子詳細資訊", + "torrents.list.context.generate.magnet": "Generate Magnet Link", + "torrents.list.context.move": "設定種子位置", + "torrents.list.context.pause": "暫停", + "torrents.list.context.download": "下載", + "torrents.list.context.priority": "優先權", + "torrents.list.context.remove": "移除", + "torrents.list.context.set.tags": "設定標籤", + "torrents.list.context.set.trackers": "Set Trackers", + "torrents.list.context.start": "開始", + "torrents.list.context.stop": "停止", + "torrents.list.no.torrents": "沒有任何種子可以顯示。", + "torrents.list.drop": "Drop files here to add them.", + "torrents.list.cannot.connect": "Cannot connect to the client.", + "torrent.list.peers": "{connected} / {total}", + "torrent.list.peers.of": "中的", + "torrents.move.button.set.location": "設定下載位置", + "torrents.move.button.state.setting": "設定中...", + "torrents.move.data.label": "移動資料", + "torrents.move.check_hash.label": "檢查雜湊", + "torrents.move.heading": "設定下載位置", + "torrents.properties.date.added": "新增日期", + "torrents.properties.comment": "評論", + "torrents.properties.creation.date": "建立日期", + "torrents.properties.directory": "Location", + "torrents.properties.download.speed": "下載速度", + "torrents.properties.download.total": "已下載", + "torrents.properties.eta": "剩餘時間", + "torrents.properties.free.disk.space": "可用磁碟空間", + "torrents.properties.hash": "雜湊", + "torrents.properties.ignore.schedule": "忽略排程", + "torrents.properties.is.private": "私有", + "torrents.properties.name": "任務名稱", + "torrents.properties.percentage": "完成比例", + "torrents.properties.ratio": "分享率", + "torrents.properties.size": "檔案大小", + "torrents.properties.tags": "標籤", + "torrents.properties.tracker.message": "Tracker 訊息", + "torrents.properties.upload.speed": "上傳速度", + "torrents.properties.upload.total": "已上傳", + "torrents.properties.seeds": "種子", + "torrents.properties.peers": "節點", + "torrents.properties.trackers": "Trackers", + "torrents.remove.are.you.sure": "你確定要移除這些種子嗎?", + "torrents.remove.delete.data": "刪除已下載檔案", + "torrents.remove.error.no.torrents.selected": "你沒有選擇任何種子。", + "torrents.remove": "移除種子", + "torrents.set.tags.button.set": "設定標籤", + "torrents.set.tags.heading": "設定標籤", + "torrents.set.tags.enter.tags": "輸入標籤", + "torrents.set.trackers.button.set": "Set Trackers", + "torrents.set.trackers.heading": "Set Trackers", + "torrents.set.trackers.enter.tracker": "Enter a tracker", + "torrents.set.trackers.loading.trackers": "Loading trackers...", + "torrents.sort.title": "排序方式", + "connection-interruption.heading": "Cannot connect to the client", + "status.diskusage.title": "磁碟空間使用量", + "status.diskusage.used": "已使用", + "status.diskusage.free": "可用", + "status.diskusage.total": "總計", + "locale.language.auto": "自動", + "locale.language.translate": "Start to translate", + "dependency.loading.notifications": "通知", + "dependency.loading.torrent.taxonomy": "種子分類", + "dependency.loading.transfer.rate.details": "詳細資料傳輸率", + "dependency.loading.transfer.history": "資料傳輸紀錄", + "dependency.loading.torrent.list": "種子列表" +} diff --git a/client/src/javascript/i18n/zh.js b/client/src/javascript/i18n/zh.js deleted file mode 100644 index 8587e2c3c..000000000 --- a/client/src/javascript/i18n/zh.js +++ /dev/null @@ -1,350 +0,0 @@ -export default { - 'actionbar.button.start.torrent': '开始 Torrent', - 'actionbar.button.stop.torrent': '停止 Torrent', - 'actionbar.button.add.torrent': '添加 Torrent', - 'actionbar.button.remove.torrent': '移除 Torrent', - - 'alert.torrent.add': `成功添加 {countElement} {count, plural, - =1 {torrent} - other {torrents} - }。`, - 'alert.torrent.add.failed': `添加 {countElement} {count, plural, - =1 {torrent} - other {torrents} - } 失败。`, - 'alert.torrent.move': `移动 {countElement} {count, plural, - =1 {torrent} - other {torrents} - } 成功。`, - 'alert.torrent.move.failed': `移动 {countElement} {count, plural, - =1 {torrent} - other {torrents} - } 失败。`, - 'alert.torrent.remove': `成功移除 {countElement} {count, plural, - =1 {torrent} - other {torrents} - }。`, - 'alert.torrent.remove.failed': `移除 {countElement} {count, plural, - =1 {torrent} - other {torrents} - } 失败。`, - 'alert.settings.saved': '设置保存成功!', - - 'auth.add.user': '增加用户', - 'auth.connectionType': 'rTorrent 连接类型', - 'auth.connectionType.tcp': 'TCP', - 'auth.connectionType.socket': 'Unix Socket', - 'auth.create.account': '创建账号', - 'auth.create.an.account': '创建一个账号', - 'auth.create.an.account.intro': 'Welcome to Flood!', - 'auth.current.user': '当前用户', - 'auth.error.username.empty': '用户名不能为空', - 'auth.log.in': 'Log In', - 'auth.login': 'Login', - 'auth.password': '密码', - 'auth.user.accounts': '用户账号', - 'auth.username': '用户名', - 'auth.admin': 'Admin', - 'auth.message.not.admin': '用户不是 Admin', - 'auth.rtorrentHost': 'rTorrent Host', - 'auth.rtorrentPort': 'rTorrent Port', - 'auth.rtorrentSocket': 'rTorrent Socket', - 'auth.rtorrentSocketPath': 'rTorrent Socket Path', - - 'button.add': '新增', - 'button.cancel': '取消', - 'button.no': '否', - 'button.save': '保存设置', - 'button.save.feed': '保存', - 'button.test': '测试', - 'button.state.adding': '增加中...', - 'button.yes': '是', - - 'connectivity.modal.title': 'Connectivity Issue', - 'connectivity.modal.content': '无法连接至 rTorrent. 请更新信息!', - - 'feeds.add.automatic.download.rule': '增加下载规则', - 'feeds.add.feed': '添加Feed', - 'feeds.applicable.feed': '可用的 Feed', - 'feeds.apply.tags': '应用的 Tags', - 'feeds.exclude.pattern': '排除模式', - 'feeds.existing.feeds': '现有的 Feeds', - 'feeds.existing.rules': '现有的规则', - 'feeds.label': '标签', - 'feeds.match.count': '{count, plural, =1 {# match} other {# matches}}', - 'feeds.match.pattern': '匹配模式', - 'feeds.match': 'Match', - 'feeds.exclude': '排除', - 'feeds.no.feeds.available': '没有可用的 feeds。', - 'feeds.no.feeds.defined': '没有定义 feeds。', - 'feeds.no.rules.defined': '没有定义规则。', - 'feeds.regEx': '正则', - 'feeds.select.feed': '选择 Feed', - 'feeds.select.interval': '间隔', - 'feeds.start.on.load': '加载并启动', - 'feeds.tabs.download.rules': '下载规则', - 'feeds.tabs.feeds': 'Feeds', - 'feeds.tabs.heading': 'Torrent 源', - 'feeds.tags': '标签', - 'feeds.time.hr': '时', - 'feeds.time.min': '分', - 'feeds.time.day': '天', - 'feeds.torrent.destination': 'Torrent 目的地', - 'feeds.url': 'URL', - 'feeds.search': '搜索词', - 'feeds.validation.invalid.regular.expression': '无效的正则表达式。', - 'feeds.validation.must.select.feed': '你必须选择一个 feed。', - 'feeds.validation.must.specify.destination': '你必须指定目的地。', - 'feeds.validation.must.specify.label': '你必须指定标签。', - 'feeds.validation.must.specify.valid.feed.url': '你必须指定有效的feed网址。', - - 'filesystem.empty.directory': '空目录.', - 'filesystem.error.eacces': 'Flood无权读取此目录。', - 'filesystem.error.enoent': '路径不存在,将会创建此目录。', - 'filesystem.fetching': '获取目录结构...', - - 'filter.all': 'All', - 'filter.status.title': '根据状态过滤', - 'filter.status.downloading': '下载中', - 'filter.status.completed': '已完成', - 'filter.status.active': '活动中', - 'filter.status.inactive': '做种中', - 'filter.status.error': '错误', - 'filter.tracker.title': '根据Tracker过滤', - 'filter.tag.title': '根据Tag过滤', - 'filter.untagged': '未标记', - - 'general.ago': 'ago', - 'general.at': 'at', - 'general.to': 'to', - 'general.of': 'of', - - 'general.clipboard.copy': '复制', - 'general.clipboard.copied': '复制成功', - - 'locale.language.en': '英语', - 'locale.language.fr': '法语', - 'locale.language.ko': '韩语', - 'locale.language.nl': '荷兰语', - 'locale.language.zh': '中文', - - 'mediainfo.execError': '在服务器上运行mediainfo时发生错误。检查mediainfo是否已经安装,并且在PTAH中正确配置!', - 'mediainfo.fetching': '获取中...', - 'mediainfo.heading': 'Mediainfo 输出', - - 'notification.torrent.finished.heading': '下载完成!', - 'notification.torrent.finished.body': '{name}', - 'notification.torrent.errored.heading': '错误报告', - 'notification.torrent.errored.body': '{name}', - 'notification.clear.all': '全部清除', - 'notification.showing': 'Showing', - - 'priority.dont.download': '不下载', - 'priority.high': '高', - 'priority.low': '低', - 'priority.normal': '正常', - - 'settings.bandwidth.slots.download.divider.label': '下载槽分配器', - 'settings.bandwidth.slots.download.global.label': '从全球下载槽', - 'settings.bandwidth.slots.download.label': '下载每个Torrent插槽', - 'settings.bandwidth.slots.heading': '槽可用性', - 'settings.bandwidth.slots.upload.divider.label': '上传槽分配器', - 'settings.bandwidth.slots.upload.global.label': '上传槽至全球', - 'settings.bandwidth.slots.upload.label': '上传每个Torrent的插槽', - 'settings.bandwidth.transferrate.dropdown.preset.download.label': '下拉预设:下载', - 'settings.bandwidth.transferrate.dropdown.preset.upload.label': '下拉预设:上传', - 'settings.bandwidth.transferrate.global.throttle.download': '全球下载比例阈值', - 'settings.bandwidth.transferrate.global.throttle.upload': '全球上传比例阈值', - 'settings.bandwidth.transferrate.heading': '传输比例阈值', - - 'settings.connectivity.dht.label': '启动 DHT', - 'settings.connectivity.dht.port.label': 'DHT 端口', - 'settings.connectivity.dpd.heading': 'Decentralized Peer Discovery', - 'settings.connectivity.incoming.heading': '传入连接', - 'settings.connectivity.ip.hostname.label': '报告 IP/Hostname', - 'settings.connectivity.max.http.connections': '最大HTTP连接', - 'settings.connectivity.peer.exchange.label': '可以Peer交换', - 'settings.connectivity.peers.desired.label': 'Peers Desired', - 'settings.connectivity.peers.heading': 'Peers', - 'settings.connectivity.peers.max.label': '最大Peers值', - 'settings.connectivity.peers.min.label': '最小Peers值', - 'settings.connectivity.peers.seeding.max.label': '最大做种数', - 'settings.connectivity.peers.seeding.min.label': '最小做种数', - 'settings.connectivity.port.open.label': '打开端口', - 'settings.connectivity.port.randomize.label': '随机端口', - 'settings.connectivity.port.range.label': '端口范围', - - 'settings.resources.disk.check.hash.label': '完成时验证Hash值', - 'settings.resources.disk.download.location.label': '默认下载目录', - 'settings.resources.disk.heading': '磁盘', - 'settings.resources.max.open.files': '打开文件最大值', - 'settings.resources.memory.heading': '内存', - 'settings.resources.memory.max.label': '最大内存使用', - - 'settings.tabs.bandwidth': '带宽', - 'settings.tabs.connectivity': '连接', - 'settings.tabs.heading': '设置', - 'settings.tabs.resources': '资源', - 'settings.tabs.authentication': '认证', - 'settings.tabs.userinterface': '用户接口', - 'settings.tabs.about': '关于', - - 'settings.ui.locale': '语言环境', - 'settings.ui.language': '语言', - 'settings.ui.torrent.list': 'Torrent 列表显示', - 'settings.ui.torrent.size': 'Torrent 尺寸', - 'settings.ui.torrent.size.expanded': '展开试图', - 'settings.ui.torrent.size.condensed': '凝聚试图', - 'settings.ui.torrent.details.enabled': '启动', - 'settings.ui.torrent.details.tags.placement': '在展开视图中,tags在列表末尾最有效。', - - 'settings.about.flood': '关于Flood', - - 'sidebar.button.feeds': '订阅', - 'sidebar.button.notifications': '通知', - 'sidebar.button.settings': '设置', - 'sidebar.button.speedlimits': '速度限制', - 'sidebar.button.log.out': '登出', - - 'sidebar.search.placeholder': '搜索 torrents', - - 'sidebar.transferdata.downloaded': '已下载', - 'sidebar.transferdata.uploaded': '已上传', - - 'speed.unlimited': '不限制', - - 'unit.size.byte': 'B', - 'unit.size.kilobyte': 'kB', - 'unit.size.megabyte': 'MB', - 'unit.size.gigabyte': 'GB', - 'unit.size.terabyte': 'TB', - 'unit.speed': '{baseUnit}/s', - 'unit.time.year': '年', - 'unit.time.week': '周', - 'unit.time.day': '天', - 'unit.time.hour': '时', - 'unit.time.minute': '分', - 'unit.time.second': '秒', - 'unit.time.infinity': '∞', - - 'torrents.add.button.add': '添加Torrent', - 'torrents.add.destination.label': '路径', - 'torrents.add.destination.placeholder': '路径', - 'torrents.add.heading': '添加 Torrents', - 'torrents.add.start.label': '启动 Torrent', - 'torrents.add.tab.file.browse': '或通过浏览器打开', - 'torrents.add.tab.file.drop': '拖拽文件到这里,', - 'torrents.add.tab.file.title': '根据文件', - 'torrents.add.tab.url.input.placeholder': 'Torrent URL或者磁力链接', - 'torrents.add.tab.url.title': '根据URL', - 'torrents.add.torrents.label': 'Torrents', - - 'torrents.destination.base_path': '作为基础路径', - - 'torrents.details.actions.pause': '暂停', - 'torrents.details.actions.start': '开始', - 'torrents.details.actions.stop': '停止', - 'torrents.details.details': '详情', - 'torrents.details.files': '文件', - 'torrents.details.files.loading': '加载文件详情...', - 'torrents.details.files.download.file': `{count, plural, - =1 {Download File} - other {Download Files} - }`, - 'torrents.details.general.comment': '评论', - 'torrents.details.general.connected': '{connected} connected of {total}', - 'torrents.details.general.date.added': '已添加', - 'torrents.details.general.date.created': '创建日期', - 'torrents.details.general.downloaded': '已下载', - 'torrents.details.general.free.disk.space': '可用磁盘空间', - 'torrents.details.general.hash': 'Hash', - 'torrents.details.general.heading.general': '概括', - 'torrents.details.general.heading.torrent': 'Torrent', - 'torrents.details.general.heading.tracker': 'Tracker', - 'torrents.details.general.heading.transfer': '传输', - 'torrents.details.general.location': '位置', - 'torrents.details.general.none': 'None', - 'torrents.details.general.peers': 'Peers', - 'torrents.details.general.scheduler.ignored': 'Ignored', - 'torrents.details.general.scheduler.obeyed': 'Obeyed', - 'torrents.details.general.scheduler': 'Scheduler', - 'torrents.details.general.seeds': '种子', - 'torrents.details.general.size': '尺寸', - 'torrents.details.general.tags': 'Tags', - 'torrents.details.general.tracker.message': 'Tracker 消息', - 'torrents.details.general.type.private': 'Private', - 'torrents.details.general.type.public': 'Public', - 'torrents.details.general.type': 'Type', - 'torrents.details.mediainfo': 'Mediainfo', - 'torrents.details.peers.no.data': '这个种子没有peer数据。', - 'torrents.details.peers': 'Peers', - 'torrents.details.selected.files': `{count, plural, - =1 {{countElement} selected file} - other {{countElement} selected files} - }`, - 'torrents.details.selected.files.set.priority': '设置优先级', - 'torrents.details.trackers.no.data': '这个种子没有tracker 数据。', - 'torrents.details.trackers.type': 'Type', - 'torrents.details.trackers': 'Trackers', - - 'torrents.list.clear.filters': '清除过滤器', - 'torrents.list.context.check.hash': '检查 Hash', - 'torrents.list.context.details': 'Torrent 详情', - 'torrents.list.context.move': '设置 Torrent 路径', - 'torrents.list.context.pause': '暂停', - 'torrents.list.context.priority': '优先级', - 'torrents.list.context.remove': '移除', - 'torrents.list.context.set.tags': '设置 Tags', - 'torrents.list.context.start': '开始', - 'torrents.list.context.stop': '停止', - 'torrents.list.no.torrents': '没有 torrents.', - 'torrents.list.drop': '移动文件以将他添加到 rTorrent.', - 'torrents.list.cannot.connect': '无法连接到 rTorrent.', - 'torrent.list.peers': '{connected} {of} {total}', - 'torrent.list.peers.of': 'of', - - 'torrents.move.button.set.location': '设置下载路径', - 'torrents.move.button.state.setting': '设置中...', - 'torrents.move.data.label': '移动数据', - 'torrents.move.heading': '设置 Torrent 路径', - - 'torrents.properties.date.added': '已添加', - 'torrents.properties.base.path': 'Base Path', - 'torrents.properties.comment': '备注', - 'torrents.properties.creation.date': '添加日期', - 'torrents.properties.download.speed': '现在速度', - 'torrents.properties.download.total': '已下载', - 'torrents.properties.eta': 'ETA', - 'torrents.properties.free.disk.space': '剩余硬盘空间', - 'torrents.properties.hash': 'Hash', - 'torrents.properties.ignore.schedule': '忽略调度程序', - 'torrents.properties.is.private': 'Private', - 'torrents.properties.name': '名称', - 'torrents.properties.percentage': '完成百分比', - 'torrents.properties.ratio': '分享率', - 'torrents.properties.size': '文件尺寸', - 'torrents.properties.tags': 'Tags', - 'torrents.properties.tracker.message': 'Tracker 信息', - 'torrents.properties.upload.speed': '上传速度', - 'torrents.properties.upload.total': '已上传', - - 'torrents.remove.are.you.sure': `你确定移动 {count, plural, - =0 {no torrents} - =1 {one torrent} - other {# torrents} - }?`, - 'torrents.remove.delete.data': '删除数据', - 'torrents.remove.error.no.torrents.selected': '你没有选中任何 torrents.', - 'torrents.remove': '移除 Torrents', - - 'torrents.set.tags.button.set': '设置 Tags', - 'torrents.set.tags.heading': '设置 Tags', - 'torrents.set.tags.enter.tags': '输入 tags', - - 'torrents.sort.title': '排序', - - 'connection-interruption.heading': '无法连接至 rTorrent', - 'connection-interruption.verify-settings-prompt': '让我们验证你的连接设置。', - 'connection-interruption.verification-error': '无法验证连接。', - 'connection-interruption.verification-success': '连接成功!', -}; diff --git a/client/src/javascript/stores/AlertStore.js b/client/src/javascript/stores/AlertStore.js deleted file mode 100644 index 98227cd79..000000000 --- a/client/src/javascript/stores/AlertStore.js +++ /dev/null @@ -1,90 +0,0 @@ -import AppDispatcher from '../dispatcher/AppDispatcher'; -import BaseStore from './BaseStore'; -import EventTypes from '../constants/EventTypes'; - -const DEFAULT_DURATION = 5 * 1000; - -class AlertStoreClass extends BaseStore { - constructor() { - super(); - - this.accumulation = {}; - this.alerts = {}; - } - - accumulate(alert) { - const {id, value} = alert.accumulation; - - if (this.accumulation[id] == null) { - this.accumulation[id] = value; - } else { - this.accumulation[id] += value; - } - } - - add(alert) { - alert.duration = alert.duration || DEFAULT_DURATION; - alert.id = alert.id || Date.now(); - - if (alert.accumulation) { - this.accumulate(alert); - } - - this.scheduleCleanse(alert); - - this.alerts[alert.id] = alert; - - this.emit(EventTypes.ALERTS_CHANGE); - } - - getAlerts() { - const alertIDs = Object.keys(this.alerts).sort(); - - return alertIDs.map(id => { - const alert = this.alerts[id]; - - if (alert.accumulation) { - alert.count = this.accumulation[alert.accumulation.id]; - } - - return alert; - }); - } - - removeExpired(alert) { - const {accumulation} = alert; - - if (accumulation) { - this.removeAccumulation(alert); - - if (this.accumulation[accumulation.id] === 0) { - delete this.accumulation[accumulation.id]; - delete this.alerts[alert.id]; - } - } else { - delete this.alerts[alert.id]; - } - - this.emit(EventTypes.ALERTS_CHANGE); - } - - removeAccumulation(alert) { - const {id, value} = alert.accumulation; - - if (this.accumulation[id] == null) { - return; - } - - this.accumulation[id] -= value; - } - - scheduleCleanse(alert) { - setTimeout(this.removeExpired.bind(this, alert), alert.duration); - } -} - -const AlertStore = new AlertStoreClass(); - -AlertStore.dispatcherID = AppDispatcher.register(() => {}); - -export default AlertStore; diff --git a/client/src/javascript/stores/AlertStore.ts b/client/src/javascript/stores/AlertStore.ts new file mode 100644 index 000000000..99aa0e922 --- /dev/null +++ b/client/src/javascript/stores/AlertStore.ts @@ -0,0 +1,65 @@ +import {computed, extendObservable, makeAutoObservable, runInAction} from 'mobx'; +import sort from 'fast-sort'; + +export interface Alert { + id: string; + type: 'success' | 'error'; + count: number; + duration: number; + timer: number; + updated: number; +} + +class AlertStore { + alerts: Record = {}; + + @computed get sortedAlerts(): Array { + return sort(Object.values(this.alerts)).asc((alert) => alert.updated); + } + + constructor() { + makeAutoObservable(this); + } + + add({ + id, + type = 'success', + count = 0, + duration = 5 * 1000, + }: { + id: string; + type?: Alert['type']; + count?: number; + duration?: number; + }) { + const curAlert = this.alerts[id]; + + if (curAlert != null) { + clearTimeout(curAlert.timer); + + curAlert.count += count; + curAlert.timer = this.scheduleClose(id, duration); + curAlert.updated = Date.now(); + } else { + const newAlert: Alert = { + id, + type, + count, + duration, + timer: this.scheduleClose(id, duration), + updated: Date.now(), + }; + extendObservable(this.alerts, {[id]: newAlert}); + } + } + + scheduleClose(id: string, duration: number): number { + return window.setTimeout(() => { + runInAction(() => { + delete this.alerts[id]; + }); + }, duration); + } +} + +export default new AlertStore(); diff --git a/client/src/javascript/stores/AuthStore.js b/client/src/javascript/stores/AuthStore.js deleted file mode 100644 index 513cc018f..000000000 --- a/client/src/javascript/stores/AuthStore.js +++ /dev/null @@ -1,169 +0,0 @@ -import ActionTypes from '../constants/ActionTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import AuthActions from '../actions/AuthActions'; -import BaseStore from './BaseStore'; -import FloodActions from '../actions/FloodActions'; -import EventTypes from '../constants/EventTypes'; - -class AuthStoreClass extends BaseStore { - constructor() { - super(); - this.isAuthenticating = false; - this.isAuthenticated = false; - this.token = null; - this.users = []; - this.optimisticUsers = []; - this.currentUser = { - isAdmin: false, - isInitialUser: false, - username: null, - }; - } - - createUser(credentials) { - AuthActions.createUser(credentials); - } - - addOptimisticUser(credentials) { - this.optimisticUsers.push({username: credentials.username}); - this.emit(EventTypes.AUTH_LIST_USERS_SUCCESS); - } - - getCurrentUsername() { - return this.currentUser.username; - } - - isAdmin() { - return this.currentUser.isAdmin; - } - - getIsAuthenticating() { - return this.isAuthenticating; - } - - getIsAuthenticated() { - return this.isAuthenticated; - } - - getIsInitialUser() { - return this.currentUser.isInitialUser; - } - - getToken() { - return this.token; - } - - getUsers() { - return this.users; - } - - handleCreateUserSuccess(data) { - this.addOptimisticUser(data); - this.emit(EventTypes.AUTH_CREATE_USER_SUCCESS); - } - - handleDeleteUserError(error) { - this.emit(EventTypes.AUTH_DELETE_USER_ERROR, error.username); - } - - handleDeleteUserSuccess(data) { - this.emit(EventTypes.AUTH_DELETE_USER_SUCCESS, data.username); - } - - handleListUsersSuccess(nextUserList) { - this.optimisticUsers = this.optimisticUsers.filter( - optimisticUser => !nextUserList.some(databaseUser => databaseUser.username === optimisticUser.username), - ); - this.users = nextUserList; - this.emit(EventTypes.AUTH_LIST_USERS_SUCCESS); - } - - handleLoginSuccess(data) { - this.currentUser.username = data.username; - this.currentUser.isAdmin = data.isAdmin; - this.currentUser.isInitialUser = false; - this.token = data.token; - this.isAuthenticating = true; - this.isAuthenticated = true; - - this.emit(EventTypes.AUTH_LOGIN_SUCCESS); - } - - handleLoginError(error) { - this.token = null; - this.isAuthenticated = false; - this.isAuthenticating = true; - this.emit(EventTypes.AUTH_LOGIN_ERROR, error); - } - - handleRegisterSuccess(data) { - this.currentUser.username = data.username; - this.currentUser.isAdmin = data.isAdmin; - this.currentUser.isInitialUser = false; - this.emit(EventTypes.AUTH_REGISTER_SUCCESS, data); - FloodActions.restartActivityStream(); - } - - handleRegisterError(error) { - this.emit(EventTypes.AUTH_REGISTER_ERROR, error); - } - - handleAuthVerificationSuccess(data) { - this.currentUser.username = data.username; - this.currentUser.isAdmin = data.isAdmin; - this.currentUser.isInitialUser = data.initialUser; - this.isAuthenticating = true; - this.isAuthenticated = !data.initialUser; - this.emit(EventTypes.AUTH_VERIFY_SUCCESS, data); - } - - handleAuthVerificationError(action) { - this.isAuthenticated = false; - this.isAuthenticating = true; - this.currentUser.isInitialUser = false; - this.emit(EventTypes.AUTH_VERIFY_ERROR, action.error); - } -} - -const AuthStore = new AuthStoreClass(); - -AuthStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - - switch (action.type) { - case ActionTypes.AUTH_LOGIN_SUCCESS: - AuthStore.handleLoginSuccess(action.data); - break; - case ActionTypes.AUTH_LOGIN_ERROR: - AuthStore.handleLoginError(action.error); - break; - case ActionTypes.AUTH_LIST_USERS_SUCCESS: - AuthStore.handleListUsersSuccess(action.data); - break; - case ActionTypes.AUTH_CREATE_USER_SUCCESS: - AuthStore.handleCreateUserSuccess(action.data); - break; - case ActionTypes.AUTH_DELETE_USER_SUCCESS: - AuthStore.handleDeleteUserSuccess(action.data); - break; - case ActionTypes.AUTH_DELETE_USER_ERROR: - AuthStore.handleDeleteUserError(action.error); - break; - case ActionTypes.AUTH_REGISTER_SUCCESS: - AuthStore.handleRegisterSuccess(action.data); - break; - case ActionTypes.AUTH_REGISTER_ERROR: - AuthStore.handleRegisterError(action.error); - break; - case ActionTypes.AUTH_VERIFY_SUCCESS: - AuthStore.handleAuthVerificationSuccess(action.data); - break; - case ActionTypes.AUTH_VERIFY_ERROR: - AuthStore.handleAuthVerificationError(action); - break; - default: - break; - } -}); - -export default AuthStore; diff --git a/client/src/javascript/stores/AuthStore.ts b/client/src/javascript/stores/AuthStore.ts new file mode 100644 index 000000000..31eef40fd --- /dev/null +++ b/client/src/javascript/stores/AuthStore.ts @@ -0,0 +1,82 @@ +import {makeAutoObservable} from 'mobx'; + +import {AccessLevel} from '@shared/schema/constants/Auth'; + +import type {AuthAuthenticationResponse, AuthVerificationResponse} from '@shared/schema/api/auth'; +import type {Credentials} from '@shared/schema/Auth'; + +import FloodActions from '../actions/FloodActions'; + +class AuthStore { + isAuthenticating = false; + isAuthenticated = false; + users: Array = []; + optimisticUsers: Array<{username: string}> = []; + currentUser: { + isAdmin: boolean; + isInitialUser: boolean; + username: string | null; + } = { + isAdmin: false, + isInitialUser: false, + username: null, + }; + + constructor() { + makeAutoObservable(this); + } + + handleCreateUserSuccess({username}: {username: Credentials['username']}): void { + this.optimisticUsers.push({username}); + } + + handleListUsersSuccess(nextUserList: Array): void { + this.optimisticUsers = this.optimisticUsers.filter( + (optimisticUser) => !nextUserList.some((databaseUser) => databaseUser.username === optimisticUser.username), + ); + this.users = nextUserList; + } + + handleLoginSuccess(response: AuthAuthenticationResponse): void { + this.currentUser.username = response.username; + this.currentUser.isAdmin = response.level === AccessLevel.ADMINISTRATOR; + this.currentUser.isInitialUser = false; + this.isAuthenticating = true; + this.isAuthenticated = true; + } + + handleLoginError(): void { + this.isAuthenticated = false; + this.isAuthenticating = true; + } + + handleRegisterSuccess(response: AuthAuthenticationResponse): void { + this.currentUser.username = response.username; + this.currentUser.isAdmin = response.level === AccessLevel.ADMINISTRATOR; + this.currentUser.isInitialUser = false; + FloodActions.restartActivityStream(); + } + + handleAuthVerificationSuccess(response: AuthVerificationResponse): void { + if (response.initialUser === true) { + this.currentUser.isInitialUser = response.initialUser; + } else { + this.currentUser = { + username: response.username, + isAdmin: response.level === AccessLevel.ADMINISTRATOR, + isInitialUser: response.initialUser, + }; + } + + this.isAuthenticating = true; + this.isAuthenticated = !this.currentUser.isInitialUser; + } + + handleAuthVerificationError(): void { + this.isAuthenticated = false; + this.isAuthenticating = true; + this.currentUser.isInitialUser = false; + } +} + +export default new AuthStore(); diff --git a/client/src/javascript/stores/BaseStore.js b/client/src/javascript/stores/BaseStore.js deleted file mode 100644 index 461fb3c8f..000000000 --- a/client/src/javascript/stores/BaseStore.js +++ /dev/null @@ -1,38 +0,0 @@ -import {EventEmitter} from 'events'; - -export default class BaseStore extends EventEmitter { - constructor(...eventEmitterConfig) { - super(...eventEmitterConfig); - - this.dispatcherID = null; - this.on('uncaughtException', error => { - throw new Error(error); - }); - this.requests = {}; - this.setMaxListeners(20); - } - - beginRequest(id) { - this.requests[id] = true; - } - - isRequestPending(id) { - if (this.requests[id] == null) { - return false; - } - - return true; - } - - listen(event, callback) { - this.on(event, callback); - } - - resolveRequest(id) { - delete this.requests[id]; - } - - unlisten(event, callback) { - this.removeListener(event, callback); - } -} diff --git a/client/src/javascript/stores/ClientStatusStore.js b/client/src/javascript/stores/ClientStatusStore.js deleted file mode 100644 index 0c33f676f..000000000 --- a/client/src/javascript/stores/ClientStatusStore.js +++ /dev/null @@ -1,39 +0,0 @@ -import ActionTypes from '../constants/ActionTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import BaseStore from './BaseStore'; -import EventTypes from '../constants/EventTypes'; - -class ClientStatusStoreClass extends BaseStore { - constructor() { - super(); - this.errorCount = 0; - this.isConnected = null; - } - - getIsConnected() { - return this.isConnected === true; - } - - handleConnectivityStatusChange({isConnected}) { - if (this.isConnected !== isConnected) { - this.isConnected = isConnected; - this.emit(EventTypes.CLIENT_CONNECTION_STATUS_CHANGE); - } - } -} - -const ClientStatusStore = new ClientStatusStoreClass(); - -ClientStatusStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - - switch (action.type) { - case ActionTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE: - ClientStatusStore.handleConnectivityStatusChange(action.data); - break; - default: - break; - } -}); - -export default ClientStatusStore; diff --git a/client/src/javascript/stores/ClientStatusStore.ts b/client/src/javascript/stores/ClientStatusStore.ts new file mode 100644 index 000000000..43f90aaf7 --- /dev/null +++ b/client/src/javascript/stores/ClientStatusStore.ts @@ -0,0 +1,17 @@ +import {makeAutoObservable} from 'mobx'; + +class ClientStatusStore { + isConnected = true; + + constructor() { + makeAutoObservable(this); + } + + handleConnectivityStatusChange({isConnected}: {isConnected: boolean}) { + if (this.isConnected !== isConnected) { + this.isConnected = isConnected === true; + } + } +} + +export default new ClientStatusStore(); diff --git a/client/src/javascript/stores/ConfigStore.js b/client/src/javascript/stores/ConfigStore.js deleted file mode 100644 index b15695478..000000000 --- a/client/src/javascript/stores/ConfigStore.js +++ /dev/null @@ -1,13 +0,0 @@ -import BaseStore from './BaseStore'; - -class ConfigStoreClass extends BaseStore { - getBaseURI() { - return process.env.BASE_URI; - } - - getPollInterval() { - return process.env.POLL_INTERVAL || 5000; - } -} - -export default new ConfigStoreClass(); diff --git a/client/src/javascript/stores/ConfigStore.ts b/client/src/javascript/stores/ConfigStore.ts new file mode 100644 index 000000000..29204f0a0 --- /dev/null +++ b/client/src/javascript/stores/ConfigStore.ts @@ -0,0 +1,52 @@ +import {computed, makeAutoObservable} from 'mobx'; + +import type {AuthMethod} from '@shared/schema/Auth'; +import type {AuthVerificationPreloadConfigs} from '@shared/schema/api/auth'; + +const queryUserPreferDark = (): boolean | null => { + const preference = window.localStorage.getItem('userPreferDark'); + + if (preference != null) { + if (preference === 'true') { + return true; + } + + if (preference === 'false') { + return false; + } + } + + return null; +}; + +class ConfigStore { + baseURI = window.location.pathname.substr(0, window.location.pathname.lastIndexOf('/') + 1); + authMethod: AuthMethod = 'default'; + pollInterval = 2000; + + systemPreferDark = false; + userPreferDark: boolean | null = queryUserPreferDark(); + @computed get preferDark(): boolean { + return this.userPreferDark ?? this.systemPreferDark; + } + + constructor() { + makeAutoObservable(this); + } + + setUserPreferDark(preference: boolean | null): void { + this.userPreferDark = preference; + if (preference == null) { + window.localStorage.removeItem('userPreferDark'); + } else { + window.localStorage.setItem('userPreferDark', `${preference}`); + } + } + + handlePreloadConfigs({authMethod, pollInterval}: AuthVerificationPreloadConfigs) { + this.authMethod = authMethod || 'default'; + this.pollInterval = pollInterval || 2000; + } +} + +export default new ConfigStore(); diff --git a/client/src/javascript/stores/DiskUsageStore.js b/client/src/javascript/stores/DiskUsageStore.js deleted file mode 100644 index 5baf9e25e..000000000 --- a/client/src/javascript/stores/DiskUsageStore.js +++ /dev/null @@ -1,35 +0,0 @@ -import BaseStore from './BaseStore'; -import ActionTypes from '../constants/ActionTypes'; -import EventTypes from '../constants/EventTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; - -class DiskUsageStoreClass extends BaseStore { - constructor() { - super(); - this.disks = []; - } - - setDiskUsage(disks) { - this.disks = disks; - this.emit(EventTypes.DISK_USAGE_CHANGE); - } - - getDiskUsage() { - return this.disks; - } -} - -const DiskUsageStore = new DiskUsageStoreClass(); - -DiskUsageStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - switch (action.type) { - case ActionTypes.DISK_USAGE_CHANGE: - DiskUsageStore.setDiskUsage(action.data); - break; - default: - break; - } -}); - -export default DiskUsageStore; diff --git a/client/src/javascript/stores/DiskUsageStore.ts b/client/src/javascript/stores/DiskUsageStore.ts new file mode 100644 index 000000000..1f896e533 --- /dev/null +++ b/client/src/javascript/stores/DiskUsageStore.ts @@ -0,0 +1,17 @@ +import {makeAutoObservable} from 'mobx'; + +import type {Disks} from '@shared/types/DiskUsage'; + +class DiskUsageStore { + disks: Disks = []; + + constructor() { + makeAutoObservable(this); + } + + setDiskUsage(disks: Disks) { + this.disks = disks; + } +} + +export default new DiskUsageStore(); diff --git a/client/src/javascript/stores/FeedMonitorStore.js b/client/src/javascript/stores/FeedMonitorStore.js deleted file mode 100644 index cea243f57..000000000 --- a/client/src/javascript/stores/FeedMonitorStore.js +++ /dev/null @@ -1,213 +0,0 @@ -import ActionTypes from '../constants/ActionTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import SettingsActions from '../actions/SettingsActions'; -import BaseStore from './BaseStore'; -import EventTypes from '../constants/EventTypes'; - -class FeedsStoreClass extends BaseStore { - constructor() { - super(); - this.feeds = []; - this.rules = []; - this.items = []; - } - - addFeed(feed) { - SettingsActions.addFeed(feed); - } - - modifyFeed(id, feed) { - SettingsActions.modifyFeed(id, feed); - } - - addRule(feed) { - SettingsActions.addRule(feed); - } - - fetchFeedMonitors(query) { - SettingsActions.fetchFeedMonitors(query); - } - - fetchFeeds(query) { - SettingsActions.fetchFeeds(query); - } - - fetchItems(query) { - SettingsActions.fetchItems(query); - } - - fetchRules(query) { - SettingsActions.fetchRules(query); - } - - removeFeed(id) { - SettingsActions.removeFeedMonitor(id); - } - - removeRule(id) { - SettingsActions.removeFeedMonitor(id); - } - - getFeeds() { - return this.feeds; - } - - getRules() { - return this.rules; - } - - getItems() { - return this.items; - } - - handleFeedAddError(error) { - this.emit(EventTypes.SETTINGS_FEED_MONITOR_FEED_ADD_ERROR, error); - } - - handleFeedAddSuccess() { - this.fetchFeedMonitors(); - this.emit(EventTypes.SETTINGS_FEED_MONITOR_FEED_ADD_SUCCESS); - } - - handleFeedModifyError(error) { - this.emit(EventTypes.SETTINGS_FEED_MONITOR_FEED_MODIFY_ERROR, error); - } - - handleFeedModifySuccess() { - this.fetchFeedMonitors(); - this.emit(EventTypes.SETTINGS_FEED_MONITOR_FEED_MODIFY_SUCCESS); - } - - handleRuleAddError(error) { - this.emit(EventTypes.SETTINGS_FEED_MONITOR_RULE_ADD_ERROR, error); - } - - handleRuleAddSuccess() { - this.fetchFeedMonitors(); - this.emit(EventTypes.SETTINGS_FEED_MONITOR_RULE_ADD_SUCCESS); - } - - handleFeedMonitorsFetchError(error) { - this.emit(EventTypes.SETTINGS_FEED_MONITORS_FETCH_ERROR, error); - } - - handleFeedMonitorsFetchSuccess(feedMonitors) { - this.setFeeds(feedMonitors.feeds); - this.setRules(feedMonitors.rules); - this.emit(EventTypes.SETTINGS_FEED_MONITORS_FETCH_SUCCESS); - } - - handleFeedMonitorRemoveError(id) { - this.emit(EventTypes.SETTINGS_FEED_MONITOR_REMOVE_ERROR, id); - } - - handleFeedMonitorRemoveSuccess(id) { - this.fetchFeedMonitors(); - this.emit(EventTypes.SETTINGS_FEED_MONITOR_REMOVE_SUCCESS, id); - } - - handleFeedsFetchError(error) { - this.emit(EventTypes.SETTINGS_FEED_MONITOR_FEEDS_FETCH_ERROR, error); - } - - handleFeedsFetchSuccess(feeds) { - this.setFeeds(feeds); - this.emit(EventTypes.SETTINGS_FEED_MONITOR_FEEDS_FETCH_SUCCESS); - } - - handleRulesFetchError(error) { - this.emit(EventTypes.SETTINGS_FEED_MONITOR_RULES_FETCH_ERROR, error); - } - - handleRulesFetchSuccess(rules) { - this.setRules(rules); - this.emit(EventTypes.SETTINGS_FEED_MONITOR_RULES_FETCH_SUCCESS); - } - - handleItemsFetchError(error) { - this.emit(EventTypes.SETTINGS_FEED_MONITOR_ITEMS_FETCH_ERROR, error); - } - - handleItemsFetchSuccess(items) { - this.items = items; - this.emit(EventTypes.SETTINGS_FEED_MONITOR_ITEMS_FETCH_SUCCESS); - } - - setItems(type, items) { - if (items == null) { - this[type] = []; - return; - } - - this[type] = items.sort((a, b) => a.label.localeCompare(b.label)); - } - - setFeeds(feeds) { - this.setItems('feeds', feeds); - } - - setRules(rules) { - this.setItems('rules', rules); - } -} - -const FeedsStore = new FeedsStoreClass(); - -FeedsStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - - switch (action.type) { - case ActionTypes.SETTINGS_FEED_MONITOR_FEED_ADD_ERROR: - FeedsStore.handleFeedAddError(action.error); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_FEED_ADD_SUCCESS: - FeedsStore.handleFeedAddSuccess(); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_FEED_MODIFY_ERROR: - FeedsStore.handleFeedModifyError(action.error); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_FEED_MODIFY_SUCCESS: - FeedsStore.handleFeedModifySuccess(); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_RULE_ADD_ERROR: - FeedsStore.handleRuleAddError(action.error); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_RULE_ADD_SUCCESS: - FeedsStore.handleRuleAddSuccess(); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_REMOVE_ERROR: - FeedsStore.handleFeedMonitorRemoveError(action.error.id); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_REMOVE_SUCCESS: - FeedsStore.handleFeedMonitorRemoveSuccess(action.data.id); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_FEEDS_FETCH_ERROR: - FeedsStore.handleFeedsFetchError(action.error); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_FEEDS_FETCH_SUCCESS: - FeedsStore.handleFeedsFetchSuccess(action.data); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_RULES_FETCH_ERROR: - FeedsStore.handleRulesFetchError(action.error); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_RULES_FETCH_SUCCESS: - FeedsStore.handleRulesFetchSuccess(action.data); - break; - case ActionTypes.SETTINGS_FEED_MONITORS_FETCH_ERROR: - FeedsStore.handleFeedMonitorsFetchError(action.error); - break; - case ActionTypes.SETTINGS_FEED_MONITORS_FETCH_SUCCESS: - FeedsStore.handleFeedMonitorsFetchSuccess(action.data); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_ITEMS_FETCH_ERROR: - FeedsStore.handleItemsFetchError(action.error); - break; - case ActionTypes.SETTINGS_FEED_MONITOR_ITEMS_FETCH_SUCCESS: - FeedsStore.handleItemsFetchSuccess(action.data); - break; - default: - break; - } -}); - -export default FeedsStore; diff --git a/client/src/javascript/stores/FeedStore.ts b/client/src/javascript/stores/FeedStore.ts new file mode 100644 index 000000000..22f50ef57 --- /dev/null +++ b/client/src/javascript/stores/FeedStore.ts @@ -0,0 +1,42 @@ +import {makeAutoObservable} from 'mobx'; + +import type {Feed, Rule, Item} from '@shared/types/Feed'; + +class FeedStore { + feeds: Array = []; + rules: Array = []; + items: Array = []; + + constructor() { + makeAutoObservable(this); + } + + setFeeds(feeds: Array): void { + if (feeds == null) { + this.feeds = []; + return; + } + + this.feeds = [...feeds].sort((a, b) => a.label.localeCompare(b.label)); + } + + setRules(rules: Array): void { + if (rules == null) { + this.rules = []; + return; + } + + this.rules = [...rules].sort((a, b) => a.label.localeCompare(b.label)); + } + + handleFeedMonitorsFetchSuccess(feedMonitors: {feeds: Array; rules: Array}): void { + this.setFeeds(feedMonitors.feeds); + this.setRules(feedMonitors.rules); + } + + handleItemsFetchSuccess(items: Array): void { + this.items = items; + } +} + +export default new FeedStore(); diff --git a/client/src/javascript/stores/NotificationStore.js b/client/src/javascript/stores/NotificationStore.js deleted file mode 100644 index 498e2b876..000000000 --- a/client/src/javascript/stores/NotificationStore.js +++ /dev/null @@ -1,81 +0,0 @@ -import ActionTypes from '../constants/ActionTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import BaseStore from './BaseStore'; -import EventTypes from '../constants/EventTypes'; -import FloodActions from '../actions/FloodActions'; - -const INTIAL_COUNT_SATE = {total: 0, unread: 0, read: 0}; - -class NotificationStoreClass extends BaseStore { - constructor() { - super(); - - this.notifications = {}; - this.notificationCount = {}; - this.ongoingPolls = {}; - } - - clearAll(options) { - this.notifications = {}; - FloodActions.clearNotifications(options); - } - - getNotificationCount() { - return this.notificationCount; - } - - getNotifications(id) { - const notificationState = this.notifications[id]; - - return { - count: INTIAL_COUNT_SATE, - ...notificationState, - }; - } - - handleNotificationCountChange(notificationCount) { - this.notificationCount = notificationCount; - this.emit(EventTypes.NOTIFICATIONS_COUNT_CHANGE, notificationCount); - } - - handleNotificationsClearSuccess(options) { - FloodActions.fetchNotifications({ - ...options, - start: 0, - }); - } - - handleNotificationsFetchError() { - this.emit(EventTypes.NOTIFICATIONS_FETCH_ERROR); - } - - handleNotificationsFetchSuccess(response) { - this.notifications[response.id] = response; - this.emit(EventTypes.NOTIFICATIONS_FETCH_SUCCESS); - } -} - -const NotificationStore = new NotificationStoreClass(); - -NotificationStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - - switch (action.type) { - case ActionTypes.FLOOD_CLEAR_NOTIFICATIONS_SUCCESS: - NotificationStore.handleNotificationsClearSuccess(action.data); - break; - case ActionTypes.FLOOD_FETCH_NOTIFICATIONS_ERROR: - NotificationStore.handleNotificationsFetchError(action.error); - break; - case ActionTypes.FLOOD_FETCH_NOTIFICATIONS_SUCCESS: - NotificationStore.handleNotificationsFetchSuccess(action.data); - break; - case ActionTypes.NOTIFICATION_COUNT_CHANGE: - NotificationStore.handleNotificationCountChange(action.data); - break; - default: - break; - } -}); - -export default NotificationStore; diff --git a/client/src/javascript/stores/NotificationStore.ts b/client/src/javascript/stores/NotificationStore.ts new file mode 100644 index 000000000..a480e0465 --- /dev/null +++ b/client/src/javascript/stores/NotificationStore.ts @@ -0,0 +1,33 @@ +import {computed, makeAutoObservable} from 'mobx'; + +import type {Notification, NotificationCount, NotificationState} from '@shared/types/Notification'; + +const INITIAL_COUNT_STATE: NotificationCount = {total: 0, unread: 0, read: 0}; + +class NotificationStore { + notifications: Array = []; + notificationCount: NotificationCount = INITIAL_COUNT_STATE; + + @computed get hasNotification() { + return this.notificationCount.total !== 0; + } + + constructor() { + makeAutoObservable(this); + } + + clearAll() { + this.notifications = []; + this.notificationCount = INITIAL_COUNT_STATE; + } + + handleNotificationCountChange(notificationCount: NotificationCount) { + this.notificationCount = notificationCount == null ? INITIAL_COUNT_STATE : notificationCount; + } + + handleNotificationsFetchSuccess(data: NotificationState) { + this.notifications = data.notifications; + } +} + +export default new NotificationStore(); diff --git a/client/src/javascript/stores/SettingStore.ts b/client/src/javascript/stores/SettingStore.ts new file mode 100644 index 000000000..838a26fcb --- /dev/null +++ b/client/src/javascript/stores/SettingStore.ts @@ -0,0 +1,54 @@ +import {computed, makeAutoObservable} from 'mobx'; + +import defaultFloodSettings from '@shared/constants/defaultFloodSettings'; + +import type {ClientSettings} from '@shared/types/ClientSettings'; +import type {FloodSettings} from '@shared/types/FloodSettings'; + +class SettingStore { + fetchStatus = { + clientSettingsFetched: false, + floodSettingsFetched: false, + }; + + clientSettings: ClientSettings | null = null; + + // Default settings are overridden by settings stored in database. + floodSettings: FloodSettings = {...defaultFloodSettings}; + + @computed get totalCellWidth() { + return this.floodSettings.torrentListColumns.reduce((accumulator, {id, visible}) => { + const width = Number(this.floodSettings.torrentListColumnWidths[id]); + + if (!visible || Number.isNaN(width)) { + return accumulator; + } + + return accumulator + width; + }, 0); + } + + constructor() { + makeAutoObservable(this); + } + + handleClientSettingsFetchSuccess(settings: ClientSettings) { + this.fetchStatus.clientSettingsFetched = true; + this.clientSettings = settings; + } + + handleSettingsFetchSuccess(settings: Partial): void { + this.fetchStatus.floodSettingsFetched = true; + Object.assign(this.floodSettings, settings); + } + + saveFloodSettings(settings: Partial) { + Object.assign(this.floodSettings, settings); + } + + saveClientSettings(settings: Partial) { + Object.assign(this.clientSettings, settings); + } +} + +export default new SettingStore(); diff --git a/client/src/javascript/stores/SettingsStore.js b/client/src/javascript/stores/SettingsStore.js deleted file mode 100644 index 65c1ed6ce..000000000 --- a/client/src/javascript/stores/SettingsStore.js +++ /dev/null @@ -1,226 +0,0 @@ -import ActionTypes from '../constants/ActionTypes'; -import AlertStore from './AlertStore'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import BaseStore from './BaseStore'; -import ClientActions from '../actions/ClientActions'; -import EventTypes from '../constants/EventTypes'; -import SettingsActions from '../actions/SettingsActions'; -import UIStore from './UIStore'; - -class SettingsStoreClass extends BaseStore { - constructor() { - super(); - - this.fetchStatus = { - clientSettingsFetched: false, - floodSettingsFetched: false, - }; - - this.clientSettings = {}; - - // Default settings are overridden by settings stored in database. - this.floodSettings = { - language: 'en', - sortTorrents: { - direction: 'desc', - property: 'dateAdded', - }, - torrentDetails: [ - {id: 'name', visible: true}, - {id: 'percentComplete', visible: true}, - {id: 'downTotal', visible: true}, - {id: 'downRate', visible: true}, - {id: 'upTotal', visible: true}, - {id: 'upRate', visible: true}, - {id: 'eta', visible: true}, - {id: 'ratio', visible: true}, - {id: 'sizeBytes', visible: true}, - {id: 'peers', visible: true}, - {id: 'seeds', visible: true}, - {id: 'dateAdded', visible: true}, - {id: 'dateCreated', visible: false}, - {id: 'basePath', visible: false}, - {id: 'comment', visible: false}, - {id: 'hash', visible: false}, - {id: 'isPrivate', visible: false}, - {id: 'message', visible: false}, - {id: 'trackerURIs', visible: false}, - {id: 'tags', visible: true}, - ], - torrentListColumnWidths: {}, - torrentListViewSize: 'condensed', - speedLimits: { - download: [1024, 10240, 102400, 512000, 1048576, 2097152, 5242880, 10485760, 0], - upload: [1024, 10240, 102400, 512000, 1048576, 2097152, 5242880, 10485760, 0], - }, - startTorrentsOnLoad: false, - mountPoints: [], - }; - } - - getClientSettings(property) { - if (property) { - return this.clientSettings[property]; - } - - return Object.assign({}, this.clientSettings); - } - - getFloodSettings(property) { - if (property) { - return this.floodSettings[property]; - } - - return Object.assign({}, this.floodSettings); - } - - handleClientSettingsFetchSuccess(settings) { - this.fetchStatus.clientSettingsFetched = true; - this.clientSettings = settings; - - this.processSettingsState(); - } - - handleClientSettingsFetchError() { - this.emit(EventTypes.CLIENT_SETTINGS_FETCH_REQUEST_ERROR); - } - - handleClientSettingsSaveRequestError() { - this.emit(EventTypes.CLIENT_SETTINGS_SAVE_REQUEST_ERROR); - } - - handleClientSettingsSaveRequestSuccess(data, options) { - this.emit(EventTypes.CLIENT_SETTINGS_SAVE_REQUEST_SUCCESS); - - if (options.alert) { - AlertStore.add({ - id: 'alert.settings.saved', - }); - } - - if (options.dismissModal) { - UIStore.dismissModal(); - } - } - - handleSettingsFetchError() { - this.emit(EventTypes.SETTINGS_FETCH_REQUEST_ERROR); - } - - handleSettingsFetchSuccess(settings) { - this.fetchStatus.floodSettingsFetched = true; - - Object.keys(settings).forEach(property => { - const incomingSettingsValue = settings[property]; - - if (incomingSettingsValue != null) { - this.floodSettings[property] = incomingSettingsValue; - } - }); - - this.emit(EventTypes.SETTINGS_FETCH_REQUEST_SUCCESS); - this.processSettingsState(); - } - - handleSettingsSaveRequestError() { - this.emit(EventTypes.SETTINGS_SAVE_REQUEST_ERROR); - } - - handleSettingsSaveRequestSuccess(data, options = {}) { - this.emit(EventTypes.SETTINGS_SAVE_REQUEST_SUCCESS); - - if (options.alert) { - AlertStore.add({ - id: 'alert.settings.saved', - }); - } - - if (options.dismissModal) { - UIStore.dismissModal(); - } - } - - processSettingsState() { - if (this.fetchStatus.clientSettingsFetched && this.fetchStatus.floodSettingsFetched) { - this.emit(EventTypes.SETTINGS_CHANGE); - } - } - - saveFloodSettings(settings, options) { - if (!Array.isArray(settings)) { - settings = [settings]; - } - - SettingsActions.saveSettings(settings, options); - this.updateLocalSettings(settings, 'floodSettings'); - this.emit(EventTypes.SETTINGS_CHANGE); - } - - saveClientSettings(settings, options) { - if (!Array.isArray(settings)) { - settings = [settings]; - } - - ClientActions.saveSettings(settings, options); - this.updateLocalSettings(settings, 'clientSettings'); - this.emit(EventTypes.SETTINGS_CHANGE); - } - - updateOptimisticallyOnly(settings, settingsType = 'floodSettings') { - if (!Array.isArray(settings)) { - settings = [settings]; - } - - this.updateLocalSettings(settings, settingsType); - } - - updateLocalSettings(settings, settingsType) { - settings.forEach(setting => { - if (setting.overrideLocalSetting) { - this[settingsType][setting.overrideID] = setting.overrideData; - } else { - this[settingsType][setting.id] = setting.data; - } - }); - } -} - -const SettingsStore = new SettingsStoreClass(); - -SettingsStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - - switch (action.type) { - case ActionTypes.CLIENT_SETTINGS_FETCH_REQUEST_ERROR: - SettingsStore.handleClientSettingsFetchError(action.error); - break; - case ActionTypes.CLIENT_SETTINGS_FETCH_REQUEST_SUCCESS: - SettingsStore.handleClientSettingsFetchSuccess(action.data); - break; - case ActionTypes.CLIENT_SET_THROTTLE_SUCCESS: - ClientActions.fetchSettings(); - break; - case ActionTypes.SETTINGS_FETCH_REQUEST_ERROR: - SettingsStore.handleSettingsFetchError(action.error); - break; - case ActionTypes.SETTINGS_FETCH_REQUEST_SUCCESS: - SettingsStore.handleSettingsFetchSuccess(action.data); - break; - case ActionTypes.SETTINGS_SAVE_REQUEST_ERROR: - SettingsStore.handleSettingsSaveRequestError(action.error); - break; - case ActionTypes.SETTINGS_SAVE_REQUEST_SUCCESS: - SettingsStore.handleSettingsSaveRequestSuccess(action.data, action.options); - break; - case ActionTypes.CLIENT_SETTINGS_SAVE_ERROR: - SettingsStore.handleClientSettingsSaveRequestError(action.error); - break; - case ActionTypes.CLIENT_SETTINGS_SAVE_SUCCESS: - SettingsStore.handleClientSettingsSaveRequestSuccess(action.data, action.options); - break; - default: - break; - } -}); - -export default SettingsStore; diff --git a/client/src/javascript/stores/TorrentFilterStore.js b/client/src/javascript/stores/TorrentFilterStore.js deleted file mode 100644 index 0d72610f4..000000000 --- a/client/src/javascript/stores/TorrentFilterStore.js +++ /dev/null @@ -1,191 +0,0 @@ -import diffActionTypes from '@shared/constants/diffActionTypes'; - -import ActionTypes from '../constants/ActionTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import BaseStore from './BaseStore'; -import EventTypes from '../constants/EventTypes'; -import SettingsStore from './SettingsStore'; -// TODO: Fix this circular dependency -// eslint-disable-next-line -import TorrentStore from './TorrentStore'; - -class TorrentFilterStoreClass extends BaseStore { - constructor() { - super(); - - this.searchFilter = ''; - this.statusFilter = 'all'; - this.taxonomy = {}; - this.tagFilter = 'all'; - this.trackerFilter = 'all'; - this.sortTorrentsBy = SettingsStore.getFloodSettings('sortTorrents'); - } - - clearAllFilters() { - this.searchFilter = ''; - this.statusFilter = 'all'; - this.tagFilter = 'all'; - this.trackerFilter = 'all'; - TorrentStore.triggerTorrentsFilter(); - this.emit(EventTypes.UI_TORRENTS_FILTER_CLEAR); - this.emit(EventTypes.UI_TORRENTS_FILTER_SEARCH_CHANGE); - this.emit(EventTypes.UI_TORRENTS_FILTER_STATUS_CHANGE); - this.emit(EventTypes.UI_TORRENTS_FILTER_TRACKER_CHANGE); - this.emit(EventTypes.UI_TORRENTS_FILTER_TAG_CHANGE); - } - - getSearchFilter() { - return this.searchFilter; - } - - getStatusFilter() { - return this.statusFilter; - } - - getTagFilter() { - return this.tagFilter; - } - - getTrackerFilter() { - return this.trackerFilter; - } - - getTorrentsSort() { - return this.sortTorrentsBy; - } - - getTorrentStatusCount() { - return this.taxonomy.statusCounts || {}; - } - - getTorrentTagCount() { - return this.taxonomy.tagCounts || {}; - } - - getTorrentTrackerCount() { - return this.taxonomy.trackerCounts || {}; - } - - handleFetchSettingsRequest() { - this.setTorrentsSort(SettingsStore.getFloodSettings('sortTorrents')); - } - - handleTorrentTaxonomyDiffChange(diff) { - Object.keys(diff).forEach(taxonomyKey => { - const changes = diff[taxonomyKey]; - - changes.forEach(change => { - if (change.action === diffActionTypes.ITEM_REMOVED) { - delete this.taxonomy[taxonomyKey][change.data]; - } else { - this.taxonomy[taxonomyKey] = { - ...this.taxonomy[taxonomyKey], - ...change.data, - }; - } - }); - }); - - // TODO: This logic is duplicated. Also update it to check for changed - // trackers. - if (this.tagFilter !== 'all' && !Object.keys(this.taxonomy.tagCounts).includes(this.tagFilter)) { - this.setTagFilter('all'); - } - - this.emit(EventTypes.CLIENT_FETCH_TORRENT_TAXONOMY_SUCCESS); - } - - handleTorrentTaxonomyFullUpdate(taxonomy) { - this.taxonomy = taxonomy; - - // TODO: This logic is duplicated. Also update it to check for changed - // trackers. - if (this.tagFilter !== 'all' && !Object.keys(taxonomy.tags).includes(this.tagFilter)) { - this.setTagFilter('all'); - } - - this.emit(EventTypes.CLIENT_FETCH_TORRENT_TAXONOMY_SUCCESS); - } - - isFilterActive() { - return ( - this.getStatusFilter() !== 'all' || - this.getSearchFilter() !== '' || - this.getTagFilter() !== 'all' || - this.getTrackerFilter() !== 'all' - ); - } - - setSearchFilter(filter) { - this.searchFilter = filter; - this.emit(EventTypes.UI_TORRENTS_FILTER_CHANGE); - this.emit(EventTypes.UI_TORRENTS_FILTER_SEARCH_CHANGE); - } - - setStatusFilter(filter) { - this.statusFilter = filter; - this.emit(EventTypes.UI_TORRENTS_FILTER_CHANGE); - this.emit(EventTypes.UI_TORRENTS_FILTER_STATUS_CHANGE); - } - - setTagFilter(filter) { - this.tagFilter = filter; - this.emit(EventTypes.UI_TORRENTS_FILTER_CHANGE); - this.emit(EventTypes.UI_TORRENTS_FILTER_TAG_CHANGE); - } - - setTrackerFilter(filter) { - this.trackerFilter = filter; - this.emit(EventTypes.UI_TORRENTS_FILTER_CHANGE); - this.emit(EventTypes.UI_TORRENTS_FILTER_TRACKER_CHANGE); - } - - setTorrentsSort(sortBy) { - this.sortTorrentsBy = sortBy; - TorrentStore.triggerTorrentsSort(); - this.emit(EventTypes.UI_TORRENTS_SORT_CHANGE); - } - - setTorrentStatusCount(statusCount) { - this.torrentStatusCount = statusCount; - this.emit(EventTypes.CLIENT_TORRENT_STATUS_COUNT_CHANGE); - } -} - -const TorrentFilterStore = new TorrentFilterStoreClass(); - -TorrentFilterStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - - switch (action.type) { - case ActionTypes.UI_SET_TORRENT_SEARCH_FILTER: - TorrentFilterStore.setSearchFilter(action.data); - break; - case ActionTypes.UI_SET_TORRENT_STATUS_FILTER: - TorrentFilterStore.setStatusFilter(action.data); - break; - case ActionTypes.UI_SET_TORRENT_TAG_FILTER: - TorrentFilterStore.setTagFilter(action.data); - break; - case ActionTypes.UI_SET_TORRENT_TRACKER_FILTER: - TorrentFilterStore.setTrackerFilter(action.data); - break; - case ActionTypes.UI_SET_TORRENT_SORT: - TorrentFilterStore.setTorrentsSort(action.data); - break; - case ActionTypes.TAXONOMY_FULL_UPDATE: - TorrentFilterStore.handleTorrentTaxonomyFullUpdate(action.data); - break; - case ActionTypes.TAXONOMY_DIFF_CHANGE: - TorrentFilterStore.handleTorrentTaxonomyDiffChange(action.data); - break; - case ActionTypes.SETTINGS_FETCH_REQUEST_SUCCESS: - AppDispatcher.waitFor([SettingsStore.dispatcherID]); - TorrentFilterStore.handleFetchSettingsRequest(); - break; - default: - break; - } -}); - -export default TorrentFilterStore; diff --git a/client/src/javascript/stores/TorrentFilterStore.ts b/client/src/javascript/stores/TorrentFilterStore.ts new file mode 100644 index 000000000..bc971c8bc --- /dev/null +++ b/client/src/javascript/stores/TorrentFilterStore.ts @@ -0,0 +1,85 @@ +import {computed, makeAutoObservable} from 'mobx'; +import jsonpatch, {Operation} from 'fast-json-patch'; + +import type {Taxonomy} from '@shared/types/Taxonomy'; +import type {TorrentStatus} from '@shared/constants/torrentStatusMap'; + +class TorrentFilterStore { + filters: { + searchFilter: string; + statusFilter: TorrentStatus | ''; + tagFilter: string; + trackerFilter: string; + } = { + searchFilter: '', + statusFilter: '', + tagFilter: '', + trackerFilter: '', + }; + + taxonomy: Taxonomy = { + statusCounts: {}, + tagCounts: {}, + trackerCounts: {}, + }; + + @computed get isFilterActive() { + return ( + this.filters.searchFilter !== '' || + this.filters.statusFilter !== '' || + this.filters.tagFilter !== '' || + this.filters.trackerFilter !== '' + ); + } + + constructor() { + makeAutoObservable(this); + } + + clearAllFilters() { + this.filters = { + searchFilter: '', + statusFilter: '', + tagFilter: '', + trackerFilter: '', + }; + } + + handleTorrentTaxonomyDiffChange(diff: Operation[]) { + jsonpatch.applyPatch(this.taxonomy, diff); + } + + handleTorrentTaxonomyFullUpdate(taxonomy: Taxonomy) { + this.taxonomy = taxonomy; + } + + setSearchFilter(filter: string) { + this.filters = { + ...this.filters, + searchFilter: filter, + }; + } + + setStatusFilter(filter: TorrentStatus) { + this.filters = { + ...this.filters, + statusFilter: filter, + }; + } + + setTagFilter(filter: string) { + this.filters = { + ...this.filters, + tagFilter: filter, + }; + } + + setTrackerFilter(filter: string) { + this.filters = { + ...this.filters, + trackerFilter: filter, + }; + } +} + +export default new TorrentFilterStore(); diff --git a/client/src/javascript/stores/TorrentStore.js b/client/src/javascript/stores/TorrentStore.js deleted file mode 100644 index e8e69b9fa..000000000 --- a/client/src/javascript/stores/TorrentStore.js +++ /dev/null @@ -1,342 +0,0 @@ -import serverEventTypes from '@shared/constants/serverEventTypes'; - -import ActionTypes from '../constants/ActionTypes'; -import AlertStore from './AlertStore'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import BaseStore from './BaseStore'; -import ConfigStore from './ConfigStore'; -import EventTypes from '../constants/EventTypes'; -import {filterTorrents} from '../util/filterTorrents'; -import {searchTorrents} from '../util/searchTorrents'; -import {selectTorrents} from '../util/selectTorrents'; -import SettingsStore from './SettingsStore'; -import {sortTorrents} from '../util/sortTorrents'; -import TorrentActions from '../actions/TorrentActions'; -// TODO: Fix this circular dependency -// eslint-disable-next-line -import TorrentFilterStore from './TorrentFilterStore'; -import UIStore from './UIStore'; - -const pollInterval = ConfigStore.getPollInterval(); - -// TODO: Handle these events in the respective stores -const handleRemoveTorrentsSuccess = response => { - SettingsStore.saveFloodSettings({ - id: 'deleteTorrentData', - data: response.deleteData, - }); - - AlertStore.add({ - accumulation: { - id: 'alert.torrent.remove', - value: response.count, - }, - id: 'alert.torrent.remove', - }); -}; - -const handleRemoveTorrentsError = error => { - AlertStore.add({ - accumulation: { - id: 'alert.torrent.remove.failed', - value: error.count, - }, - id: 'alert.torrent.remove.failed', - }); -}; - -class TorrentStoreClass extends BaseStore { - constructor() { - super(); - - this.filteredTorrents = {}; - this.mediainfo = {}; - this.pollTorrentDetailsIntervalID = null; - this.selectedTorrents = []; - this.sortedTorrents = []; - this.torrents = {}; - } - - fetchTorrentDetails(options = {}) { - if (!this.isRequestPending('fetch-torrent-details') || options.forceUpdate) { - this.beginRequest('fetch-torrent-details'); - TorrentActions.fetchTorrentDetails(UIStore.getTorrentDetailsHash()); - } - - if (this.pollTorrentDetailsIntervalID === null) { - this.startPollingTorrentDetails(); - } - } - - filterTorrents() { - const searchFilter = TorrentFilterStore.getSearchFilter(); - const statusFilter = TorrentFilterStore.getStatusFilter(); - const tagFilter = TorrentFilterStore.getTagFilter(); - const trackerFilter = TorrentFilterStore.getTrackerFilter(); - - let filteredTorrents = Object.assign([], this.sortedTorrents); - - if (searchFilter && searchFilter !== '') { - filteredTorrents = searchTorrents(filteredTorrents, searchFilter); - } - - if (statusFilter && statusFilter !== 'all') { - filteredTorrents = filterTorrents(filteredTorrents, { - type: 'status', - filter: statusFilter, - }); - } - - if (tagFilter && tagFilter !== 'all') { - filteredTorrents = filterTorrents(filteredTorrents, { - type: 'tag', - filter: tagFilter, - }); - } - - if (trackerFilter && trackerFilter !== 'all') { - filteredTorrents = filterTorrents(filteredTorrents, { - type: 'tracker', - filter: trackerFilter, - }); - } - - this.filteredTorrents = filteredTorrents; - } - - getTorrentDetails(hash) { - return this.torrents[hash].details || null; - } - - getSelectedTorrents() { - return this.selectedTorrents; - } - - getSelectedTorrentsDownloadLocations() { - return this.selectedTorrents.map(hash => this.torrents[hash].basePath); - } - - getSelectedTorrentsFilename() { - return this.selectedTorrents.map(hash => this.torrents[hash].baseFilename); - } - - getSelectedTorrentsTags() { - return this.selectedTorrents.map(hash => this.torrents[hash].tags); - } - - handleAddTorrentError() { - this.emit(EventTypes.CLIENT_ADD_TORRENT_ERROR); - } - - handleAddTorrentSuccess(response) { - this.emit(EventTypes.CLIENT_ADD_TORRENT_SUCCESS); - - SettingsStore.saveFloodSettings({ - id: 'torrentDestination', - data: response.destination, - }); - - AlertStore.add({ - accumulation: { - id: 'alert.torrent.add', - value: response.count || 1, - }, - id: 'alert.torrent.add', - }); - } - - handleFetchMediainfoSuccess(response) { - this.mediainfo[response.hash] = response.output; - this.emit(EventTypes.FLOOD_FETCH_MEDIAINFO_SUCCESS); - } - - getTorrent(hash) { - return this.torrents[hash]; - } - - getAllTorrents() { - return this.torrents; - } - - getMediainfo(hash) { - return this.mediainfo[hash]; - } - - getTorrents() { - // TODO: Audit this filteredTorrents vs sortedTorrents concept. - if (TorrentFilterStore.isFilterActive()) { - return this.filteredTorrents; - } - - return this.sortedTorrents; - } - - handleMoveTorrentsSuccess(response) { - this.emit(EventTypes.CLIENT_MOVE_TORRENTS_SUCCESS); - - AlertStore.add({ - accumulation: { - id: 'alert.torrent.move', - value: response.count, - }, - id: 'alert.torrent.move', - }); - } - - handleMoveTorrentsError(error) { - this.emit(EventTypes.CLIENT_MOVE_TORRENTS_REQUEST_ERROR); - - AlertStore.add({ - accumulation: { - id: 'alert.torrent.move.failed', - value: error.count, - }, - id: 'alert.torrent.move.failed', - }); - } - - setSelectedTorrents(event, hash) { - this.selectedTorrents = selectTorrents({ - event, - hash, - selectedTorrents: this.selectedTorrents, - torrentList: this.filteredTorrents, - }); - this.emit(EventTypes.UI_TORRENT_SELECTION_CHANGE); - } - - handleSetFilePrioritySuccess() { - this.emit(EventTypes.CLIENT_SET_FILE_PRIORITY_SUCCESS); - this.fetchTorrentDetails({forceUpdate: true}); - } - - handleTorrentListDiffChange(torrentListDiff) { - Object.keys(torrentListDiff).forEach(torrentHash => { - const {action, data} = torrentListDiff[torrentHash]; - - switch (action) { - case serverEventTypes.TORRENT_LIST_ACTION_TORRENT_ADDED: - this.torrents[torrentHash] = data; - break; - case serverEventTypes.TORRENT_LIST_ACTION_TORRENT_DELETED: - if (this.selectedTorrents.includes(torrentHash)) { - this.selectedTorrents = this.selectedTorrents.filter(hash => hash !== torrentHash); - } - - delete this.torrents[torrentHash]; - break; - case serverEventTypes.TORRENT_LIST_ACTION_TORRENT_DETAIL_UPDATED: - Object.keys(data).forEach(detailKey => { - this.torrents[torrentHash][detailKey] = data[detailKey]; - }); - break; - default: - break; - } - }); - - this.sortTorrents(); - this.filterTorrents(); - - this.emit(EventTypes.CLIENT_TORRENTS_REQUEST_SUCCESS); - } - - handleTorrentListFullUpdate(torrentList) { - this.torrents = torrentList; - - this.sortTorrents(); - this.filterTorrents(); - - this.emit(EventTypes.CLIENT_TORRENTS_REQUEST_SUCCESS); - } - - setTorrentDetails(hash, torrentDetails) { - this.torrents[hash].details = torrentDetails; - this.resolveRequest('fetch-torrent-details'); - this.emit(EventTypes.CLIENT_TORRENT_DETAILS_CHANGE); - } - - sortTorrents() { - const sortBy = TorrentFilterStore.getTorrentsSort(); - - // Convert torrents hash to array and sort it. - this.sortedTorrents = sortTorrents(this.torrents, sortBy); - } - - startPollingTorrentDetails() { - this.pollTorrentDetailsIntervalID = setInterval(this.fetchTorrentDetails.bind(this), pollInterval); - } - - stopPollingTorrentDetails() { - clearInterval(this.pollTorrentDetailsIntervalID); - this.pollTorrentDetailsIntervalID = null; - } - - triggerTorrentsFilter() { - this.filterTorrents(); - this.emit(EventTypes.UI_TORRENTS_LIST_FILTERED); - } - - triggerTorrentsSort() { - this.sortTorrents(); - this.triggerTorrentsFilter(); - } -} - -const TorrentStore = new TorrentStoreClass(); - -TorrentStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - - switch (action.type) { - case ActionTypes.CLIENT_FETCH_TORRENT_DETAILS_SUCCESS: - TorrentStore.setTorrentDetails(action.data.hash, action.data.torrentDetails); - break; - case ActionTypes.CLIENT_ADD_TORRENT_ERROR: - TorrentStore.handleAddTorrentError(action.error); - break; - case ActionTypes.CLIENT_ADD_TORRENT_SUCCESS: - TorrentStore.handleAddTorrentSuccess(action.data); - break; - case ActionTypes.TORRENT_LIST_DIFF_CHANGE: - TorrentStore.handleTorrentListDiffChange(action.data); - break; - case ActionTypes.TORRENT_LIST_FULL_UPDATE: - TorrentStore.handleTorrentListFullUpdate(action.data); - break; - case ActionTypes.CLIENT_MOVE_TORRENTS_SUCCESS: - TorrentStore.handleMoveTorrentsSuccess(action.data); - break; - case ActionTypes.CLIENT_MOVE_TORRENTS_ERROR: - TorrentStore.handleMoveTorrentsError(action.error); - break; - case ActionTypes.CLIENT_REMOVE_TORRENT_SUCCESS: - handleRemoveTorrentsSuccess(action.data); - break; - case ActionTypes.CLIENT_REMOVE_TORRENT_ERROR: - handleRemoveTorrentsError(action.error); - break; - case ActionTypes.CLIENT_SET_FILE_PRIORITY_SUCCESS: - TorrentStore.handleSetFilePrioritySuccess(action.data); - break; - case ActionTypes.FLOOD_FETCH_MEDIAINFO_SUCCESS: - TorrentStore.handleFetchMediainfoSuccess(action.data); - break; - case ActionTypes.UI_CLICK_TORRENT: - TorrentStore.setSelectedTorrents(action.data.event, action.data.hash); - break; - case ActionTypes.UI_SET_TORRENT_SORT: - TorrentStore.triggerTorrentsSort(); - break; - case ActionTypes.UI_SET_TORRENT_SEARCH_FILTER: - case ActionTypes.UI_SET_TORRENT_STATUS_FILTER: - case ActionTypes.UI_SET_TORRENT_TAG_FILTER: - case ActionTypes.UI_SET_TORRENT_TRACKER_FILTER: - TorrentStore.triggerTorrentsFilter(); - break; - default: - break; - } -}); - -export default TorrentStore; diff --git a/client/src/javascript/stores/TorrentStore.ts b/client/src/javascript/stores/TorrentStore.ts new file mode 100644 index 000000000..909314281 --- /dev/null +++ b/client/src/javascript/stores/TorrentStore.ts @@ -0,0 +1,76 @@ +import {applyPatch, Operation} from 'fast-json-patch'; +import {computed, makeAutoObservable} from 'mobx'; + +import type {TorrentProperties, TorrentList} from '@shared/types/Torrent'; + +import filterTorrents from '../util/filterTorrents'; +import searchTorrents from '../util/searchTorrents'; +import selectTorrents from '../util/selectTorrents'; +import SettingStore from './SettingStore'; +import sortTorrents from '../util/sortTorrents'; +import TorrentFilterStore from './TorrentFilterStore'; + +class TorrentStore { + selectedTorrents: Array = []; + torrents: TorrentList = {}; + + constructor() { + makeAutoObservable(this); + } + + @computed get sortedTorrents(): Array { + return sortTorrents(Object.values(this.torrents), SettingStore.floodSettings.sortTorrents); + } + + @computed get filteredTorrents(): Array { + const {searchFilter, statusFilter, tagFilter, trackerFilter} = TorrentFilterStore.filters; + + let filteredTorrents = Object.assign([], this.sortedTorrents) as Array; + + if (searchFilter !== '') { + filteredTorrents = searchTorrents(filteredTorrents, searchFilter); + } + + if (statusFilter !== '') { + filteredTorrents = filterTorrents(filteredTorrents, { + type: 'status', + filter: statusFilter, + }); + } + + if (tagFilter !== '') { + filteredTorrents = filterTorrents(filteredTorrents, { + type: 'tag', + filter: tagFilter, + }); + } + + if (trackerFilter !== '') { + filteredTorrents = filterTorrents(filteredTorrents, { + type: 'tracker', + filter: trackerFilter, + }); + } + + return filteredTorrents; + } + + setSelectedTorrents({event, hash}: {event: React.MouseEvent | React.TouchEvent; hash: string}) { + this.selectedTorrents = selectTorrents({ + event, + hash, + selectedTorrents: this.selectedTorrents, + torrentList: this.filteredTorrents, + }); + } + + handleTorrentListDiffChange(torrentListDiffs: Operation[]) { + applyPatch(this.torrents, torrentListDiffs); + } + + handleTorrentListFullUpdate(torrentList: TorrentList) { + this.torrents = torrentList; + } +} + +export default new TorrentStore(); diff --git a/client/src/javascript/stores/TransferDataStore.js b/client/src/javascript/stores/TransferDataStore.js deleted file mode 100644 index 5fa9b9061..000000000 --- a/client/src/javascript/stores/TransferDataStore.js +++ /dev/null @@ -1,103 +0,0 @@ -import diffActionTypes from '@shared/constants/diffActionTypes'; - -import ActionTypes from '../constants/ActionTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import BaseStore from './BaseStore'; -import EventTypes from '../constants/EventTypes'; - -class TransferDataStoreClass extends BaseStore { - constructor() { - super(); - - this.transferRates = {download: [], upload: [], timestamps: []}; - this.transferSummary = {}; - } - - appendCurrentTransferRateToHistory() { - // TODO: Find a better way to append the current transfer rate. This - // just replaces the last transfer rate values from the history service with - // the most recent speed. - if (this.transferRates.download.length > 0) { - this.transferRates.download[this.transferRates.download.length - 1] = this.transferSummary.downRate; - this.transferRates.upload[this.transferRates.upload.length - 1] = this.transferSummary.upRate; - } - - this.emit(EventTypes.CLIENT_TRANSFER_HISTORY_REQUEST_SUCCESS); - } - - getTransferSummary() { - return this.transferSummary; - } - - getTransferRates() { - return this.transferRates; - } - - handleSetThrottleSuccess() { - this.emit(EventTypes.CLIENT_SET_THROTTLE_SUCCESS); - } - - handleSetThrottleError() { - this.emit(EventTypes.CLIENT_SET_THROTTLE_ERROR); - } - - handleFetchTransferHistoryError() { - this.emit(EventTypes.CLIENT_TRANSFER_HISTORY_REQUEST_ERROR); - } - - handleFetchTransferHistorySuccess(transferData) { - this.transferRates = transferData; - this.emit(EventTypes.CLIENT_TRANSFER_HISTORY_REQUEST_SUCCESS); - } - - handleTransferSummaryDiffChange(diff) { - diff.forEach(change => { - if (change.action === diffActionTypes.ITEM_REMOVED) { - delete this.transferSummary[change.data]; - } else { - this.transferSummary = { - ...this.transferSummary, - ...change.data, - }; - } - }); - - this.appendCurrentTransferRateToHistory(); - this.emit(EventTypes.CLIENT_TRANSFER_SUMMARY_CHANGE); - } - - handleTransferSummaryFullUpdate(transferSummary) { - this.transferSummary = transferSummary; - - this.appendCurrentTransferRateToHistory(); - this.emit(EventTypes.CLIENT_TRANSFER_SUMMARY_CHANGE); - } -} - -const TransferDataStore = new TransferDataStoreClass(); - -TransferDataStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - - switch (action.type) { - case ActionTypes.TRANSFER_SUMMARY_DIFF_CHANGE: - TransferDataStore.handleTransferSummaryDiffChange(action.data); - break; - case ActionTypes.TRANSFER_SUMMARY_FULL_UPDATE: - TransferDataStore.handleTransferSummaryFullUpdate(action.data); - break; - case ActionTypes.CLIENT_SET_THROTTLE_SUCCESS: - TransferDataStore.handleSetThrottleSuccess(action.data.transferData); - break; - case ActionTypes.CLIENT_SET_THROTTLE_ERROR: - TransferDataStore.handleSetThrottleError(action.data.error); - break; - case ActionTypes.TRANSFER_HISTORY_FULL_UPDATE: - TransferDataStore.handleFetchTransferHistorySuccess(action.data); - break; - default: - break; - } -}); - -export default TransferDataStore; diff --git a/client/src/javascript/stores/TransferDataStore.ts b/client/src/javascript/stores/TransferDataStore.ts new file mode 100644 index 000000000..67eaee992 --- /dev/null +++ b/client/src/javascript/stores/TransferDataStore.ts @@ -0,0 +1,59 @@ +import jsonpatch, {Operation} from 'fast-json-patch'; +import {makeAutoObservable} from 'mobx'; + +import type {TransferDirection, TransferHistory, TransferSummary} from '@shared/types/TransferData'; + +export const TRANSFER_DIRECTIONS: Readonly> = ['download', 'upload'] as const; + +class TransferDataStore { + transferRates: TransferHistory = { + download: new Array(30).fill(0), + upload: new Array(30).fill(0), + timestamps: new Array(30).fill(Date.now()), + }; + + transferSummary: TransferSummary = { + downRate: 0, + downTotal: 0, + upRate: 0, + upTotal: 0, + }; + + constructor() { + makeAutoObservable(this); + } + + appendCurrentTransferRateToHistory() { + const download = this.transferRates.download.slice(); + const upload = this.transferRates.upload.slice(); + const timestamps = this.transferRates.timestamps.slice(); + + download.push(this.transferSummary.downRate); + upload.push(this.transferSummary.upRate); + timestamps.push(Date.now()); + + if (timestamps.length > 30) { + download.shift(); + upload.shift(); + timestamps.shift(); + } + + this.transferRates = {download, upload, timestamps}; + } + + handleFetchTransferHistorySuccess(transferData: TransferHistory) { + this.transferRates = transferData; + } + + handleTransferSummaryDiffChange(diff: Operation[]) { + jsonpatch.applyPatch(this.transferSummary, diff); + this.appendCurrentTransferRateToHistory(); + } + + handleTransferSummaryFullUpdate(transferSummary: TransferSummary) { + this.transferSummary = transferSummary; + this.appendCurrentTransferRateToHistory(); + } +} + +export default new TransferDataStore(); diff --git a/client/src/javascript/stores/UIStore.js b/client/src/javascript/stores/UIStore.js deleted file mode 100644 index 2595ba1ca..000000000 --- a/client/src/javascript/stores/UIStore.js +++ /dev/null @@ -1,210 +0,0 @@ -import ActionTypes from '../constants/ActionTypes'; -import AppDispatcher from '../dispatcher/AppDispatcher'; -import BaseStore from './BaseStore'; -import EventTypes from '../constants/EventTypes'; - -class UIStoreClass extends BaseStore { - constructor(...storeConfig) { - super(...storeConfig); - - this.activeContextMenu = null; - this.activeDropdownMenu = null; - this.activeModal = null; - this.dependencies = {}; - this.globalStyles = []; - this.haveUIDependenciesResolved = false; - this.latestTorrentLocation = null; - this.torrentDetailsHash = null; - this.createStyleElement(); - } - - addGlobalStyle(cssString) { - this.globalStyles.push(cssString); - this.applyStyles(); - } - - applyStyles() { - const {globalStyles, styleElement} = this; - const nextStyleString = globalStyles.join(''); - - while (styleElement.firstChild) { - styleElement.removeChild(styleElement.firstChild); - } - - if (styleElement.styleSheet) { - styleElement.styleSheet.cssText = nextStyleString; - } else { - styleElement.appendChild(global.document.createTextNode(nextStyleString)); - } - } - - createStyleElement() { - if (this.styleElement == null) { - const stylesheetRef = global.document.createElement('style'); - stylesheetRef.type = 'text/css'; - - global.document.head.appendChild(stylesheetRef); - - this.styleElement = stylesheetRef; - } - } - - dismissContextMenu(menuID) { - if (this.activeContextMenu.id === menuID) { - this.activeContextMenu = null; - - this.emit(EventTypes.UI_CONTEXT_MENU_CHANGE); - } - } - - dismissModal() { - this.setActiveModal(null); - } - - getActiveContextMenu() { - return this.activeContextMenu; - } - - getActiveModal() { - return this.activeModal; - } - - getActiveDropdownMenu() { - return this.activeDropdownMenu; - } - - getDependencies() { - return this.dependencies; - } - - getLatestTorrentLocation() { - return this.latestTorrentLocation; - } - - getTorrentDetailsHash() { - return this.torrentDetailsHash; - } - - handleSetTaxonomySuccess() { - if (this.activeModal.id === 'set-taxonomy') { - this.dismissModal(); - } - } - - handleTorrentClick(hash) { - this.torrentDetailsHash = hash; - this.emit(EventTypes.UI_TORRENT_DETAILS_HASH_CHANGE); - } - - hasSatisfiedDependencies() { - return Object.keys(this.dependencies).length === 0; - } - - removeGlobalStyle(cssString) { - this.globalStyles = this.globalStyles.filter(style => style !== cssString); - - this.applyStyles(); - } - - registerDependency(dependencies) { - if (!Array.isArray(dependencies)) { - dependencies = [dependencies]; - } - - dependencies.forEach(dependency => { - const {id} = dependency; - - if (!this.dependencies[id]) { - this.dependencies[id] = {...dependency, satisfied: false}; - } - }); - - this.emit(EventTypes.UI_DEPENDENCIES_CHANGE); - } - - satisfyDependency(dependencyID) { - if (this.dependencies[dependencyID] && !this.dependencies[dependencyID].satisfied) { - this.dependencies[dependencyID].satisfied = true; - this.emit(EventTypes.UI_DEPENDENCIES_CHANGE); - this.verifyDependencies(); - } - } - - setActiveContextMenu(contextMenu) { - this.activeContextMenu = contextMenu; - this.emit(EventTypes.UI_CONTEXT_MENU_CHANGE); - } - - setActiveDropdownMenu(dropdownMenu = {}) { - this.activeDropdownMenu = dropdownMenu; - this.emit(EventTypes.UI_DROPDOWN_MENU_CHANGE); - } - - setActiveModal(modal = {}) { - if (modal == null) { - this.emit(EventTypes.UI_MODAL_DISMISSED); - } - - this.activeModal = modal; - this.emit(EventTypes.UI_MODAL_CHANGE); - } - - verifyDependencies() { - const isDependencyLoading = Object.keys(this.dependencies).some(id => this.dependencies[id].satisfied === false); - - if (!isDependencyLoading) { - this.haveUIDependenciesResolved = true; - this.emit(EventTypes.UI_DEPENDENCIES_LOADED); - } - } -} - -const UIStore = new UIStoreClass(); - -UIStore.dispatcherID = AppDispatcher.register(payload => { - const {action} = payload; - - switch (action.type) { - case ActionTypes.UI_CLICK_TORRENT: - UIStore.handleTorrentClick(action.data.hash); - break; - case ActionTypes.UI_DISPLAY_DROPDOWN_MENU: - UIStore.setActiveDropdownMenu(action.data); - break; - case ActionTypes.UI_DISPLAY_MODAL: - UIStore.setActiveModal(action.data); - break; - case ActionTypes.CLIENT_SET_TAXONOMY_SUCCESS: - UIStore.handleSetTaxonomySuccess(); - break; - case ActionTypes.CLIENT_ADD_TORRENT_SUCCESS: - case ActionTypes.CLIENT_MOVE_TORRENTS_SUCCESS: - UIStore.dismissModal(); - break; - case ActionTypes.UI_DISMISS_CONTEXT_MENU: - UIStore.dismissContextMenu(action.data); - break; - case ActionTypes.UI_DISPLAY_CONTEXT_MENU: - UIStore.setActiveContextMenu(action.data); - break; - case ActionTypes.NOTIFICATION_COUNT_CHANGE: - UIStore.satisfyDependency('notifications'); - break; - case ActionTypes.TAXONOMY_FULL_UPDATE: - UIStore.satisfyDependency('torrent-taxonomy'); - break; - case ActionTypes.TORRENT_LIST_FULL_UPDATE: - UIStore.satisfyDependency('torrent-list'); - break; - case ActionTypes.TRANSFER_SUMMARY_FULL_UPDATE: - UIStore.satisfyDependency('transfer-data'); - break; - case ActionTypes.TRANSFER_HISTORY_FULL_UPDATE: - UIStore.satisfyDependency('transfer-history'); - break; - default: - break; - } -}); - -export default UIStore; diff --git a/client/src/javascript/stores/UIStore.ts b/client/src/javascript/stores/UIStore.ts new file mode 100644 index 000000000..1fb09ba7c --- /dev/null +++ b/client/src/javascript/stores/UIStore.ts @@ -0,0 +1,194 @@ +import {makeAutoObservable} from 'mobx'; +import {FC, MouseEvent} from 'react'; + +import type {TorrentContextMenuAction} from '../constants/TorrentContextMenuActions'; + +export type ContextMenuItem = + | { + type: 'action'; + action: TorrentContextMenuAction; + label: string; + labelAction?: FC; + labelSecondary?: FC; + clickHandler(event: MouseEvent): void; + dismissMenu?: boolean; + } + | { + type: 'separator'; + }; + +export interface ActiveContextMenu { + id: string; + clickPosition: { + x: number; + y: number; + }; + items: Array; +} + +export interface Dependency { + id: string; + message: {id: string} | string; + satisfied?: boolean; +} + +export type Dependencies = Record; + +interface BaseModalAction { + content: React.ReactNode; + triggerDismiss?: boolean; +} + +interface CheckboxModalAction extends BaseModalAction { + type: 'checkbox'; + id?: string; + checked?: boolean; + clickHandler?: ((event: React.MouseEvent | KeyboardEvent) => void) | null; +} + +interface ButtonModalAction extends BaseModalAction { + type: 'primary' | 'tertiary'; + isLoading?: boolean; + submit?: boolean; + clickHandler?: ((event: React.MouseEvent) => void) | null; +} + +export type ModalAction = CheckboxModalAction | ButtonModalAction; + +export type Modal = + | { + id: + | 'feeds' + | 'generate-magnet' + | 'move-torrents' + | 'remove-torrents' + | 'set-taxonomy' + | 'set-trackers' + | 'settings'; + } + | { + id: 'add-torrents'; + initialURLs?: Array<{id: number; value: string}>; + } + | { + id: 'confirm'; + content: React.ReactNode; + heading: React.ReactNode; + actions: Array; + } + | { + id: 'torrent-details'; + hash: string; + }; + +class UIStore { + activeContextMenu: ActiveContextMenu | null = null; + activeDropdownMenu: string | null = null; + activeModal: Modal | null = null; + dependencies: Dependencies = {}; + globalStyles: Array = []; + haveUIDependenciesResolved = false; + styleElement: HTMLStyleElement & { + styleSheet?: {cssText: string}; + } = this.createStyleElement(); + + constructor() { + makeAutoObservable(this); + } + + addGlobalStyle(cssString: string) { + this.globalStyles.push(cssString); + this.applyStyles(); + } + + applyStyles() { + const {globalStyles, styleElement} = this; + const nextStyleString = globalStyles.join(''); + + if (styleElement == null) { + return; + } + + while (styleElement.firstChild) { + styleElement.removeChild(styleElement.firstChild); + } + + if (styleElement.styleSheet) { + styleElement.styleSheet.cssText = nextStyleString; + } else { + styleElement.appendChild(document.createTextNode(nextStyleString)); + } + } + + createStyleElement() { + if (this.styleElement == null) { + const stylesheetRef = document.createElement('style'); + stylesheetRef.type = 'text/css'; + + document.head.appendChild(stylesheetRef); + + return stylesheetRef; + } + return this.styleElement; + } + + dismissContextMenu(menuID: ActiveContextMenu['id']) { + if (this.activeContextMenu != null && this.activeContextMenu.id === menuID) { + this.activeContextMenu = null; + } + } + + dismissModal() { + this.setActiveModal(null); + } + + handleSetTaxonomySuccess() { + if (this.activeModal != null && this.activeModal.id === 'set-taxonomy') { + this.dismissModal(); + } + } + + removeGlobalStyle(cssString: string) { + this.globalStyles = this.globalStyles.filter((style) => style !== cssString); + this.applyStyles(); + } + + registerDependency(dependencies: Array>) { + dependencies.forEach((dependency) => { + const {id} = dependency; + + if (!this.dependencies[id]) { + this.dependencies[id] = {...dependency, satisfied: false}; + } + }); + } + + satisfyDependency(dependencyID: string) { + if (this.dependencies[dependencyID] && !this.dependencies[dependencyID].satisfied) { + this.dependencies[dependencyID].satisfied = true; + this.verifyDependencies(); + } + } + + setActiveContextMenu(contextMenu: this['activeContextMenu']) { + this.activeContextMenu = contextMenu; + } + + setActiveDropdownMenu(dropdownMenu: this['activeDropdownMenu']) { + this.activeDropdownMenu = dropdownMenu; + } + + setActiveModal(modal: this['activeModal']) { + this.activeModal = modal; + } + + verifyDependencies() { + const isDependencyLoading = Object.keys(this.dependencies).some((id) => this.dependencies[id].satisfied === false); + + if (!isDependencyLoading) { + this.haveUIDependenciesResolved = true; + } + } +} + +export default new UIStore(); diff --git a/client/src/javascript/typings.d.ts b/client/src/javascript/typings.d.ts new file mode 100644 index 000000000..533a4a641 --- /dev/null +++ b/client/src/javascript/typings.d.ts @@ -0,0 +1,5 @@ +declare module '*.md' { + export const react: any; + const value: any; + export default value; +} diff --git a/client/src/javascript/ui/components/Button.tsx b/client/src/javascript/ui/components/Button.tsx new file mode 100644 index 000000000..84f8af78f --- /dev/null +++ b/client/src/javascript/ui/components/Button.tsx @@ -0,0 +1,147 @@ +import classnames from 'classnames'; +import * as React from 'react'; + +import FadeIn from './FadeIn'; +import FormElementAddon from './FormElementAddon'; +import FormRowItem from './FormRowItem'; +import LoadingRing from '../icons/LoadingRing'; + +export type ButtonProps = Pick, 'disabled' | 'onClick' | 'onChange'> & { + buttonRef?: React.Ref; + isLoading?: boolean; + additionalClassNames?: string; + labelOffset?: boolean; + addonPlacement?: 'before' | 'after'; + priority?: 'primary' | 'secondary' | 'tertiary' | 'quaternary'; + type?: 'submit' | 'button'; + + wrap?: boolean; + wrapper?: string | React.FunctionComponent; + wrapperProps?: Record; + grow?: boolean; + shrink?: boolean; +}; + +export default class Button extends React.Component { + static defaultProps = { + additionalClassNames: '', + disabled: false, + grow: false, + labelOffset: false, + priority: 'primary', + shrink: false, + type: 'button', + wrap: true, + wrapper: FormRowItem, + wrapperProps: {width: 'auto'}, + }; + + getButtonContent() { + const {children, addonPlacement} = this.props; + const buttonContent = React.Children.toArray(children).reduce( + ( + accumulator: { + addonNodes: Array; + childNodes: Array; + }, + child, + ) => { + const childAsElement = child as React.ReactElement; + if (childAsElement.type === FormElementAddon) { + accumulator.addonNodes.push( + React.cloneElement(childAsElement, { + addonPlacement, + key: childAsElement.props.className, + }), + ); + } else { + accumulator.childNodes.push(child); + } + + return accumulator; + }, + { + addonNodes: [], + childNodes: [], + }, + ); + + return { + childNode: ( +
    + {buttonContent.childNodes} +
    + ), + addonNodes: buttonContent.addonNodes, + }; + } + + doesButtonContainIcon() { + const {children} = this.props; + return React.Children.toArray(children).some((child) => { + const childAsElement = child as React.ReactElement; + return childAsElement.type === FormElementAddon; + }); + } + + render() { + const { + type, + additionalClassNames, + buttonRef, + labelOffset, + addonPlacement, + priority, + isLoading, + disabled, + wrap, + wrapper, + wrapperProps, + shrink, + grow, + onClick, + } = this.props; + const classes = classnames('button form__element', additionalClassNames, { + 'form__element--label-offset': labelOffset, + 'form__element--has-addon': addonPlacement, + [`form__element--has-addon--placed-${addonPlacement}`]: addonPlacement, + [`button--${priority}`]: priority, + 'button--is-loading': isLoading, + 'button--is-disabled': disabled, + }); + const {addonNodes, childNode} = this.getButtonContent(); + + const content = ( +
    + + {addonNodes} +
    + ); + + if (wrap) { + const WrapperComponent = wrapper as React.FunctionComponent; + return ( + + {content} + + ); + } + + return content; + } +} diff --git a/client/src/javascript/ui/components/Checkbox.tsx b/client/src/javascript/ui/components/Checkbox.tsx new file mode 100644 index 000000000..4aaf304ce --- /dev/null +++ b/client/src/javascript/ui/components/Checkbox.tsx @@ -0,0 +1,14 @@ +import {FC} from 'react'; + +import Checkmark from '../icons/Checkmark'; +import ToggleInput from './ToggleInput'; + +import type {ToggleInputProps} from './ToggleInput'; + +type CheckboxProps = Omit; + +const Checkbox: FC = (props: CheckboxProps) => ( + } /> +); + +export default Checkbox; diff --git a/client/src/javascript/ui/components/Container.tsx b/client/src/javascript/ui/components/Container.tsx new file mode 100644 index 000000000..bbebde4ea --- /dev/null +++ b/client/src/javascript/ui/components/Container.tsx @@ -0,0 +1,13 @@ +import classnames from 'classnames'; +import {FC, ReactNode} from 'react'; + +interface ContainerProps { + children: ReactNode; +} + +const Container: FC = ({children}: ContainerProps) => { + const classes = classnames('container'); + return
    {children}
    ; +}; + +export default Container; diff --git a/client/src/javascript/ui/components/ContextMenu.tsx b/client/src/javascript/ui/components/ContextMenu.tsx new file mode 100644 index 000000000..eadac0934 --- /dev/null +++ b/client/src/javascript/ui/components/ContextMenu.tsx @@ -0,0 +1,157 @@ +import CSSTransition from 'react-transition-group/CSSTransition'; +import classnames from 'classnames'; +import {CSSProperties, forwardRef, MouseEvent, ReactNode, RefObject} from 'react'; + +import Overlay from './Overlay'; +import transitionTimeouts from '../constants/transitionTimeouts'; + +import type {OverlayProps} from './Overlay'; + +const minPreferableBottomSpace = 150; +const minPreferableHorizontalSpace = 200; + +interface ContextMenuProps { + children: ReactNode; + isIn: boolean; + menuAlign?: 'left' | 'right'; + triggerCoordinates?: { + x: number; + y: number; + }; + triggerRef?: RefObject; + matchTriggerWidth?: boolean; + padding?: boolean; + scrolling?: boolean; + overlayProps?: OverlayProps; + onClick?: (event: MouseEvent) => void; + onOverlayClick?: (event: MouseEvent) => void; + onOverlayRightClick?: (event: MouseEvent) => void; +} + +const ContextMenu = forwardRef( + ( + { + children, + isIn, + matchTriggerWidth, + menuAlign, + padding, + scrolling, + triggerRef, + triggerCoordinates, + onClick, + onOverlayClick, + onOverlayRightClick, + overlayProps, + }: ContextMenuProps, + ref, + ) => { + const dropdownStyle: CSSProperties = {}; + let shouldRenderAbove = false; + + if (triggerRef?.current) { + const buttonBoundingRect = triggerRef.current.getBoundingClientRect(); + const windowHeight = window.innerHeight; + const spaceAbove = buttonBoundingRect.top; + const spaceBelow = windowHeight - buttonBoundingRect.bottom; + + shouldRenderAbove = spaceBelow < minPreferableBottomSpace && spaceAbove > spaceBelow; + + if (shouldRenderAbove) { + dropdownStyle.top = 'auto'; + dropdownStyle.bottom = spaceBelow + buttonBoundingRect.height + 5; + dropdownStyle.maxHeight = buttonBoundingRect.top - 10; + } else { + dropdownStyle.top = buttonBoundingRect.bottom + 5; + dropdownStyle.maxHeight = spaceBelow - 10; + } + + if (matchTriggerWidth) { + dropdownStyle.width = buttonBoundingRect.width; + dropdownStyle.left = buttonBoundingRect.left; + dropdownStyle.right = window.innerWidth - buttonBoundingRect.left - buttonBoundingRect.width; + } else if (menuAlign === 'right') { + dropdownStyle.right = window.innerWidth - buttonBoundingRect.left - buttonBoundingRect.width; + } else { + dropdownStyle.left = buttonBoundingRect.left; + } + } else if (triggerCoordinates) { + const windowHeight = window.innerHeight; + const windowWidth = window.innerWidth; + const spaceAbove = triggerCoordinates.y; + const spaceBelow = windowHeight - spaceAbove; + + shouldRenderAbove = spaceBelow < minPreferableBottomSpace && spaceAbove > spaceBelow; + + if (shouldRenderAbove) { + dropdownStyle.top = 'auto'; + dropdownStyle.bottom = spaceBelow; + dropdownStyle.maxHeight = spaceAbove - 10; + } else { + dropdownStyle.top = spaceAbove; + dropdownStyle.maxHeight = spaceBelow - 10; + } + + if (menuAlign === 'right' || windowWidth - triggerCoordinates.x < minPreferableHorizontalSpace) { + dropdownStyle.right = windowWidth - triggerCoordinates.x; + } else { + dropdownStyle.left = triggerCoordinates.x; + } + } + + const classes = classnames('context-menu__items', { + 'context-menu__items--is-up': shouldRenderAbove, + 'context-menu__items--is-down': !shouldRenderAbove, + 'context-menu__items--match-trigger-width': matchTriggerWidth, + 'context-menu__items--no-padding': !padding, + 'context-menu__items--no-scrolling': !scrolling, + }); + + return ( + +
    + +
    + {children} +
    +
    +
    + ); + }, +); + +ContextMenu.defaultProps = { + matchTriggerWidth: true, + menuAlign: 'left', + overlayProps: {}, + padding: true, + scrolling: true, + triggerRef: undefined, + triggerCoordinates: { + x: 0, + y: 0, + }, + onClick: undefined, + onOverlayClick: undefined, + onOverlayRightClick: undefined, +}; + +export default ContextMenu; diff --git a/client/src/javascript/ui/components/ContextMenuItem.tsx b/client/src/javascript/ui/components/ContextMenuItem.tsx new file mode 100644 index 000000000..4655e568a --- /dev/null +++ b/client/src/javascript/ui/components/ContextMenuItem.tsx @@ -0,0 +1,18 @@ +import classnames from 'classnames'; +import * as React from 'react'; + +export default class ContextMenuItem extends React.PureComponent<{ + className?: string; + onClick: React.MouseEventHandler; +}> { + render() { + const {onClick, children, className} = this.props; + const classes = classnames('context-menu__item', className); + + return ( +
    + {children} +
    + ); + } +} diff --git a/client/src/javascript/ui/components/FadeIn.tsx b/client/src/javascript/ui/components/FadeIn.tsx new file mode 100644 index 000000000..b31ded25d --- /dev/null +++ b/client/src/javascript/ui/components/FadeIn.tsx @@ -0,0 +1,15 @@ +import CSSTransition, {CSSTransitionProps} from 'react-transition-group/CSSTransition'; +import {FC, ReactNode} from 'react'; + +interface FadeInProps { + children: ReactNode; + isIn: CSSTransitionProps['in']; +} + +const FadeIn: FC = ({children, isIn}: FadeInProps) => ( + + {children} + +); + +export default FadeIn; diff --git a/client/src/javascript/ui/components/Form.tsx b/client/src/javascript/ui/components/Form.tsx new file mode 100644 index 000000000..54087ce82 --- /dev/null +++ b/client/src/javascript/ui/components/Form.tsx @@ -0,0 +1,86 @@ +import * as React from 'react'; + +import {getDataFromForm, resetFormData} from './util/forms'; + +interface FormProps { + className?: string; + onChange?: ({ + event, + formData, + }: { + event: Event | React.FormEvent; + formData: Record; + }) => void; + onSubmit?: ({ + event, + formData, + }: { + event: Event | React.FormEvent; + formData: Record; + }) => void; +} + +class Form extends React.Component { + formRef?: HTMLFormElement | null = null; + componentDidMount() { + if (this.formRef != null) { + this.formRef.addEventListener('flood-form-change', this.handleFormChange); + } + } + + componentWillUnmount() { + if (this.formRef != null) { + this.formRef.removeEventListener('flood-form-change', this.handleFormChange); + } + } + + getFormData = (): Record => { + if (this.formRef != null) { + return getDataFromForm(this.formRef); + } + return {}; + }; + + resetForm = () => { + if (this.formRef != null) { + resetFormData(this.formRef); + } + }; + + handleFormChange = (event: Event | React.FormEvent) => { + if (this.formRef != null && this.props.onChange) { + const formData = getDataFromForm(this.formRef); + this.props.onChange({event, formData}); + } + }; + + handleFormSubmit = (event: Event | React.FormEvent) => { + event.preventDefault(); + + if (this.props.onSubmit) { + const formData = getDataFromForm(event.target as HTMLFormElement); + this.props.onSubmit({event, formData}); + } + }; + + setFormRef = (ref: HTMLFormElement) => { + this.formRef = ref; + }; + + render() { + const {children, className} = this.props; + + return ( +
    + {children} +
    + ); + } +} + +export default Form; diff --git a/client/src/javascript/ui/components/FormElementAddon.tsx b/client/src/javascript/ui/components/FormElementAddon.tsx new file mode 100644 index 000000000..a1a03f6d3 --- /dev/null +++ b/client/src/javascript/ui/components/FormElementAddon.tsx @@ -0,0 +1,50 @@ +import classnames from 'classnames'; +import {FC, HTMLAttributes, ReactNode} from 'react'; + +interface FormElementAddonProps { + children: ReactNode; + addonPlacement?: 'before' | 'after'; + addonIndex?: number; + className?: string; + isInteractive?: boolean; + type?: 'icon'; + onClick?: HTMLAttributes['onClick']; +} + +const FormElementAddon: FC = ({ + children, + type, + addonPlacement, + addonIndex, + className, + isInteractive, + onClick, +}: FormElementAddonProps) => { + const classes = classnames( + 'form__element__addon', + { + [`form__element__addon--placed-${addonPlacement}`]: addonPlacement, + [`form__element__addon--index-${addonIndex}`]: addonIndex, + 'form__element__addon--is-interactive': isInteractive || onClick, + 'form__element__addon--is-icon': type === 'icon', + }, + className, + ); + + return ( +
    + {children} +
    + ); +}; + +FormElementAddon.defaultProps = { + type: 'icon', + isInteractive: false, + addonPlacement: undefined, + addonIndex: undefined, + className: undefined, + onClick: undefined, +}; + +export default FormElementAddon; diff --git a/client/src/javascript/ui/components/FormError.tsx b/client/src/javascript/ui/components/FormError.tsx new file mode 100644 index 000000000..628ea068c --- /dev/null +++ b/client/src/javascript/ui/components/FormError.tsx @@ -0,0 +1,25 @@ +import classnames from 'classnames'; +import {PureComponent} from 'react'; + +import FormRowItem from './FormRowItem'; + +interface FormErrorProps { + isLoading?: boolean; +} + +class FormError extends PureComponent { + render() { + // Maybe add some classes later. + const classes = classnames('form__element error', { + 'error--is-loading': this.props.isLoading, + }); + + return ( + + {this.props.children} + + ); + } +} + +export default FormError; diff --git a/client/src/javascript/ui/components/FormGroup.tsx b/client/src/javascript/ui/components/FormGroup.tsx new file mode 100644 index 000000000..e35681f2f --- /dev/null +++ b/client/src/javascript/ui/components/FormGroup.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; + +import FormRowItem from './FormRowItem'; + +import type {FormRowItemProps} from './FormRowItem'; + +export default class FormRowItemGroup extends React.Component<{ + label?: string; + width?: FormRowItemProps['width']; +}> { + getLabel(): React.ReactNode { + const {label} = this.props; + + if (label) { + return ; + } + return undefined; + } + + render() { + const {children, width} = this.props; + + return ( + + {this.getLabel()} + {children} + + ); + } +} diff --git a/client/src/javascript/ui/components/FormRow.tsx b/client/src/javascript/ui/components/FormRow.tsx new file mode 100644 index 000000000..6b54c93c9 --- /dev/null +++ b/client/src/javascript/ui/components/FormRow.tsx @@ -0,0 +1,27 @@ +import classnames from 'classnames'; +import {FC, ReactNode} from 'react'; + +interface FormRowProps { + children: ReactNode; + align?: 'start' | 'center' | 'end'; + justify?: 'start' | 'center' | 'end'; + wrap?: boolean; +} + +const FormRow: FC = ({children, align, justify, wrap}: FormRowProps) => { + const classes = classnames('form__row', { + 'form__row--wrap': wrap, + [`form__row--justify--${justify}`]: justify, + [`form__row--align--${align}`]: align, + }); + + return
    {children}
    ; +}; + +FormRow.defaultProps = { + align: 'start', + justify: 'start', + wrap: false, +}; + +export default FormRow; diff --git a/client/src/javascript/ui/components/FormRowGroup.tsx b/client/src/javascript/ui/components/FormRowGroup.tsx new file mode 100644 index 000000000..425796d1a --- /dev/null +++ b/client/src/javascript/ui/components/FormRowGroup.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; + +interface FormRowGroupProps { + children: React.ReactNode; +} + +const FormRowGroup = React.forwardRef(({children}: FormRowGroupProps, ref) => ( +
    + {children} +
    +)); + +export default FormRowGroup; diff --git a/client/src/javascript/ui/components/FormRowItem.tsx b/client/src/javascript/ui/components/FormRowItem.tsx new file mode 100644 index 000000000..2715ede5a --- /dev/null +++ b/client/src/javascript/ui/components/FormRowItem.tsx @@ -0,0 +1,46 @@ +import classnames from 'classnames'; +import * as React from 'react'; + +export interface FormRowItemProps { + children: React.ReactNode; + + className?: string; + type?: string; + + grow?: boolean; + shrink?: boolean; + width?: + | 'auto' + | 'one-eighth' + | 'one-quarter' + | 'three-eighths' + | 'one-half' + | 'five-eighths' + | 'three-quarters' + | 'seven-eighths'; +} + +const FormRowItem = React.forwardRef( + ({children, className, type, width, grow, shrink}: FormRowItemProps, ref) => { + const classes = classnames('form__row__item', className, { + [`form__row__item--${width}`]: width, + 'form__row__item--grow': grow, + 'form__row__item--shrink': shrink, + 'form__row__item--error': type === 'error', + }); + + return ( +
    + {children} +
    + ); + }, +); + +FormRowItem.defaultProps = { + grow: true, + shrink: true, + width: 'auto', +}; + +export default FormRowItem; diff --git a/client/src/javascript/ui/components/Overlay.tsx b/client/src/javascript/ui/components/Overlay.tsx new file mode 100644 index 000000000..b9f128b1c --- /dev/null +++ b/client/src/javascript/ui/components/Overlay.tsx @@ -0,0 +1,40 @@ +import classnames from 'classnames'; +import {FC, MouseEvent, ReactNode} from 'react'; + +export interface OverlayProps { + children?: ReactNode; + additionalClassNames?: string; + isInteractive?: boolean; + isTransparent?: boolean; + onClick?: (event: MouseEvent) => void; + onContextMenu?: (event: MouseEvent) => void; +} + +const Overlay: FC = ({ + children, + additionalClassNames, + onClick, + onContextMenu, + isInteractive, + isTransparent, +}: OverlayProps) => { + const classes = classnames('overlay', additionalClassNames, { + 'overlay--no-interaction': !isInteractive, + 'overlay--transparent': isTransparent, + }); + + return ( +
    + {children} +
    + ); +}; + +Overlay.defaultProps = { + additionalClassNames: undefined, + isInteractive: true, + isTransparent: false, + onClick: undefined, +}; + +export default Overlay; diff --git a/client/src/javascript/ui/components/Panel.tsx b/client/src/javascript/ui/components/Panel.tsx new file mode 100644 index 000000000..12166e590 --- /dev/null +++ b/client/src/javascript/ui/components/Panel.tsx @@ -0,0 +1,25 @@ +import classnames from 'classnames'; +import {PureComponent} from 'react'; + +interface PanelProps { + theme?: 'light' | 'dark'; + spacing?: 'small' | 'medium' | 'large'; + transparent?: boolean; +} + +class Panel extends PureComponent { + static defaultProps = { + spacing: 'medium', + theme: 'light', + }; + + render() { + const classes = classnames(`panel panel--${this.props.theme}`, `panel--${this.props.spacing}`, { + 'panel--transparent': this.props.transparent, + }); + + return
    {this.props.children}
    ; + } +} + +export default Panel; diff --git a/client/src/javascript/ui/components/PanelContent.tsx b/client/src/javascript/ui/components/PanelContent.tsx new file mode 100644 index 000000000..6f2c73fa0 --- /dev/null +++ b/client/src/javascript/ui/components/PanelContent.tsx @@ -0,0 +1,22 @@ +import classnames from 'classnames'; +import {PureComponent} from 'react'; + +interface PanelContentProps { + hasBorder?: boolean; + borderPosition?: string; +} + +export default class PanelContent extends PureComponent { + static defaultProps = { + hasBorder: false, + borderPosition: 'top', + }; + + render() { + const classes = classnames(`panel__content`, { + [`panel__content--has-border--${this.props.borderPosition}`]: this.props.hasBorder, + }); + + return
    {this.props.children}
    ; + } +} diff --git a/client/src/javascript/ui/components/PanelFooter.tsx b/client/src/javascript/ui/components/PanelFooter.tsx new file mode 100644 index 000000000..b91dbbdd0 --- /dev/null +++ b/client/src/javascript/ui/components/PanelFooter.tsx @@ -0,0 +1,16 @@ +import classnames from 'classnames'; +import {PureComponent} from 'react'; + +interface PanelFooterProps { + hasBorder?: boolean; +} + +export default class PanelFooter extends PureComponent { + render() { + const classes = classnames('panel__footer', { + 'panel__footer--has-border': this.props.hasBorder, + }); + + return
    {this.props.children}
    ; + } +} diff --git a/client/src/javascript/ui/components/PanelHeader.tsx b/client/src/javascript/ui/components/PanelHeader.tsx new file mode 100644 index 000000000..4ef702bc1 --- /dev/null +++ b/client/src/javascript/ui/components/PanelHeader.tsx @@ -0,0 +1,22 @@ +import classnames from 'classnames'; +import {PureComponent} from 'react'; + +interface PanelHeaderProps { + hasBorder?: boolean; + level?: 1 | 2 | 3 | 4 | 5 | 6; +} + +export default class PanelHeader extends PureComponent { + static defaultProps = { + hasBorder: false, + level: 1, + }; + + render() { + const classes = classnames(`panel__header panel__header--level-${this.props.level}`, { + 'panel__header--has-border': this.props.hasBorder, + }); + + return
    {this.props.children}
    ; + } +} diff --git a/client/src/javascript/ui/components/Portal.tsx b/client/src/javascript/ui/components/Portal.tsx new file mode 100644 index 000000000..387aa6982 --- /dev/null +++ b/client/src/javascript/ui/components/Portal.tsx @@ -0,0 +1,34 @@ +import ReactDOM from 'react-dom'; +import * as React from 'react'; + +interface PortalProps { + children: React.ReactNode; +} + +class Portal extends React.Component { + mountPoint: HTMLDivElement | null = null; + + componentDidMount() { + this.mountPoint = document.createElement('div'); + this.mountPoint.classList.add('portal'); + document.body.appendChild(this.mountPoint); + } + + componentWillUnmount() { + if (this.mountPoint == null) { + return; + } + ReactDOM.unmountComponentAtNode(this.mountPoint); + document.body.removeChild(this.mountPoint); + } + + render() { + if (this.mountPoint == null) return null; + + const {children} = this.props; + + return ReactDOM.createPortal(children, this.mountPoint); + } +} + +export default Portal; diff --git a/client/src/javascript/ui/components/Radio.tsx b/client/src/javascript/ui/components/Radio.tsx new file mode 100644 index 000000000..695ba3b11 --- /dev/null +++ b/client/src/javascript/ui/components/Radio.tsx @@ -0,0 +1,18 @@ +import {FC} from 'react'; + +import Circle from '../icons/Circle'; +import ToggleInput from './ToggleInput'; + +import type {ToggleInputProps} from './ToggleInput'; + +type RadioProps = Omit & { + id: Required; + groupID: Required; +}; + +const Radio: FC = (props: RadioProps) => { + const {groupID, id} = props; + return } id={groupID} type="radio" value={id} />; +}; + +export default Radio; diff --git a/client/src/javascript/ui/components/Select.tsx b/client/src/javascript/ui/components/Select.tsx new file mode 100644 index 000000000..47eca0196 --- /dev/null +++ b/client/src/javascript/ui/components/Select.tsx @@ -0,0 +1,277 @@ +import noop from 'lodash/noop'; +import classnames from 'classnames'; +import {Component, cloneElement, createRef, ReactElement, ReactNode, ReactNodeArray, Children} from 'react'; + +import Button from './Button'; +import ContextMenu from './ContextMenu'; +import {dispatchChangeEvent} from './util/forms'; +import FormElementAddon from './FormElementAddon'; +import Chevron from '../icons/Chevron'; +import FormRowItem from './FormRowItem'; +import Portal from './Portal'; +import SelectItem from './SelectItem'; + +import type {FormRowItemProps} from './FormRowItem'; +import type {ButtonProps} from './Button'; + +interface SelectProps { + id: string | number; + defaultID?: string | number; + additionalClassNames?: string; + width?: FormRowItemProps['width']; + priority?: ButtonProps['priority']; + onOpen?: () => void; + onClose?: () => void; + onSelect?: (id: this['id']) => void; + label?: ReactNode; + menuAlign?: 'left' | 'right'; + disabled?: boolean; + persistentPlaceholder?: boolean; + matchTriggerWidth?: boolean; + shrink?: boolean; + grow?: boolean; + labelOffset?: boolean; +} + +interface SelectStates { + isOpen: boolean; + selectedID: string | number; +} + +export default class Select extends Component { + menuRef = createRef(); + + inputRef = createRef(); + + triggerRef = createRef(); + + static defaultProps = { + persistentPlaceholder: false, + priority: 'quaternary', + }; + + constructor(props: SelectProps) { + super(props); + + this.state = { + isOpen: false, + selectedID: this.getInitialSelectedID(), + }; + } + + componentDidUpdate(_prevProps: SelectProps, prevState: SelectStates) { + const {onOpen, onClose} = this.props; + const {isOpen} = this.state; + + if (isOpen && !prevState.isOpen) { + window.addEventListener('keydown', this.handleKeyDown); + window.addEventListener('scroll', this.handleWindowScroll, { + capture: true, + }); + + if (onOpen) { + onOpen(); + } + } else if (!isOpen && prevState.isOpen) { + window.addEventListener('keydown', this.handleKeyDown); + window.removeEventListener('scroll', this.handleWindowScroll, { + capture: true, + }); + + if (onClose) { + onClose(); + } + } + } + + getInitialSelectedID(): string | number { + const {children, defaultID} = this.props; + + if (defaultID != null) { + return defaultID; + } + + const childArray = children as ReactNodeArray; + if (childArray != null) { + const item = childArray.find((child) => (child as SelectItem).props.id != null) as SelectItem; + + if (item?.props?.id != null) { + return item.props.id; + } + } + + return ''; + } + + getItemList(children: ReactNodeArray) { + return children.reduce((accumulator: Array, child) => { + const item = child as SelectItem; + + if (item.props.placeholder) { + return accumulator; + } + + const {selectedID} = this.state; + + accumulator.push( + cloneElement(child as ReactElement, { + onClick: this.handleItemClick, + isSelected: item.props.id === selectedID, + }), + ); + + return accumulator; + }, []); + } + + getLabel(): ReactNode { + const {id, label} = this.props; + + if (label) { + return ( + + ); + } + + return undefined; + } + + getSelectedItem(children: ReactNodeArray): ReactElement | undefined { + const {persistentPlaceholder} = this.props; + const {selectedID} = this.state; + + const selectedItem = children.find((child, index) => { + const item = child as SelectItem; + return ( + (persistentPlaceholder && item.props.placeholder) || + (!selectedID && index === 0) || + item.props.id === selectedID + ); + }); + + if (selectedItem) { + return cloneElement(selectedItem as ReactElement, {isTrigger: true}); + } + + return undefined; + } + + getTrigger(selectItems: ReactNodeArray) { + const {priority} = this.props; + const selectedItem = this.getSelectedItem(selectItems); + + return ( + + ); + } + + handleTriggerClick = () => { + if (!this.props.disabled) { + this.toggleOpenState(); + } + }; + + handleItemClick = (id: string | number) => { + this.setState({isOpen: false, selectedID: id}, () => { + if (this.props.onSelect) { + this.props.onSelect(id); + } + + if (this.inputRef.current) { + dispatchChangeEvent(this.inputRef.current); + } + }); + }; + + handleKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + event.preventDefault(); + + this.setState({isOpen: false}); + } + }; + + handleOverlayClick = () => { + this.toggleOpenState(); + }; + + handleWindowScroll = (event: Event) => { + if (this.menuRef.current && !this.menuRef.current.contains(event.target as Node)) { + if (this.state.isOpen) { + this.setState({isOpen: false}); + } + } + }; + + toggleOpenState = () => { + const wasOpen = this.state.isOpen; + this.setState({ + isOpen: !wasOpen, + }); + }; + + render() { + const { + additionalClassNames, + children, + disabled, + labelOffset, + shrink, + grow, + matchTriggerWidth, + width, + id, + menuAlign, + } = this.props; + const {isOpen, selectedID} = this.state; + + const selectItems = Children.toArray(children); + const classes = classnames('select form__element', additionalClassNames, { + 'form__element--disabled': disabled, + 'form__element--label-offset': labelOffset, + 'select--is-open': isOpen, + }); + + return ( + + {this.getLabel()} +
    + + {this.getTrigger(selectItems)} + + + {this.getItemList(selectItems)} + + +
    +
    + ); + } +} diff --git a/client/src/javascript/ui/components/SelectItem.tsx b/client/src/javascript/ui/components/SelectItem.tsx new file mode 100644 index 000000000..d7eaaf76d --- /dev/null +++ b/client/src/javascript/ui/components/SelectItem.tsx @@ -0,0 +1,48 @@ +import classnames from 'classnames'; +import {Component} from 'react'; + +import Checkmark from '../icons/Checkmark'; +import ContextMenuItem from './ContextMenuItem'; + +interface SelectItemProps { + id?: string | number; + isSelected?: boolean; + isTrigger?: boolean; + placeholder?: boolean; + onClick?: (id: this['id']) => void; +} + +export default class SelectItem extends Component { + static defaultProps = { + isTrigger: false, + }; + + handleClick = () => { + if (!this.props.onClick) { + return; + } + + this.props.onClick(this.props.id); + }; + + render() { + const {children, isTrigger, isSelected} = this.props; + + let icon = null; + if (!isTrigger && isSelected) { + icon = ; + } + + const classes = classnames({ + 'select__item context-menu__item': !isTrigger, + 'select__item--is-selected': isSelected, + }); + + return ( + + {icon} + {children} + + ); + } +} diff --git a/client/src/javascript/ui/components/Textbox.tsx b/client/src/javascript/ui/components/Textbox.tsx new file mode 100644 index 000000000..28acfd7cb --- /dev/null +++ b/client/src/javascript/ui/components/Textbox.tsx @@ -0,0 +1,102 @@ +import classnames from 'classnames'; +import {Children, cloneElement, forwardRef, ReactElement} from 'react'; + +import FormElementAddon from './FormElementAddon'; +import FormRowItem from './FormRowItem'; + +import type {FormRowItemProps} from './FormRowItem'; + +type TextboxProps = Pick< + React.InputHTMLAttributes, + 'children' | 'disabled' | 'defaultValue' | 'placeholder' | 'readOnly' | 'onChange' | 'onClick' | 'autoComplete' +> & { + id: string; + label?: React.ReactNode; + type?: 'text' | 'password'; + width?: FormRowItemProps['width']; + addonPlacement?: 'before' | 'after'; + labelOffset?: boolean; + wrapperClassName?: string; +}; + +const Textbox = forwardRef( + ( + { + children, + id, + label, + addonPlacement, + labelOffset, + wrapperClassName, + width, + defaultValue, + placeholder, + autoComplete, + type, + disabled, + readOnly, + onChange, + onClick, + }: TextboxProps, + ref, + ) => { + let addonCount = 0; + const childElements = Children.map(children, (child) => { + const childAsElement = child as ReactElement; + if (childAsElement && childAsElement.type === FormElementAddon) { + addonCount += 1; + return cloneElement(childAsElement, { + addonIndex: addonCount, + addonPlacement, + }); + } + + return child; + }); + + const inputClasses = classnames('input input--text form__element', { + [`form__element--has-addon--placed-${addonPlacement}`]: addonPlacement && children, + [`form__element--has-addon--count-${addonCount}`]: addonCount > 0, + 'form__element--label-offset': labelOffset, + }); + const wrapperClasses = classnames('form__element__wrapper', wrapperClassName); + + return ( + + {label != null ? ( + + ) : undefined} +
    + + {childElements} +
    +
    + ); + }, +); + +Textbox.defaultProps = { + label: undefined, + type: 'text', + width: undefined, + addonPlacement: undefined, + labelOffset: undefined, + wrapperClassName: undefined, +}; + +export default Textbox; diff --git a/client/src/javascript/ui/components/ToggleInput.tsx b/client/src/javascript/ui/components/ToggleInput.tsx new file mode 100644 index 000000000..666a4b5fe --- /dev/null +++ b/client/src/javascript/ui/components/ToggleInput.tsx @@ -0,0 +1,88 @@ +import classnames from 'classnames'; +import {FC, InputHTMLAttributes, MouseEvent, ReactNode, useRef} from 'react'; + +import {dispatchChangeEvent} from './util/forms'; +import FormRowItem from './FormRowItem'; + +import type {FormRowItemProps} from './FormRowItem'; + +export interface ToggleInputProps { + children?: ReactNode; + id?: InputHTMLAttributes['name']; + groupID?: InputHTMLAttributes['name']; + type: 'checkbox' | 'radio'; + value?: InputHTMLAttributes['value']; + defaultChecked?: InputHTMLAttributes['defaultChecked']; + checked?: InputHTMLAttributes['checked']; + shrink?: FormRowItemProps['shrink']; + grow?: FormRowItemProps['grow']; + width?: FormRowItemProps['width']; + icon: JSX.Element; + matchTextboxHeight?: boolean; + labelOffset?: boolean; + onClick?: (event: MouseEvent) => void; +} + +const ToggleInput: FC = ({ + children, + id, + groupID, + type, + value, + defaultChecked, + checked, + shrink, + grow, + width, + icon, + matchTextboxHeight, + labelOffset, + onClick, +}: ToggleInputProps) => { + const inputRef = useRef(null); + const classes = classnames('form__element toggle-input', type, { + 'form__element--match-textbox-height': matchTextboxHeight, + 'form__element--label-offset': labelOffset, + }); + + return ( + + + + ); +}; + +ToggleInput.defaultProps = { + onClick: () => { + // do nothing. + }, + grow: false, + shrink: false, +}; + +export default ToggleInput; diff --git a/client/src/javascript/ui/components/util/forms.tsx b/client/src/javascript/ui/components/util/forms.tsx new file mode 100644 index 000000000..4d6f604da --- /dev/null +++ b/client/src/javascript/ui/components/util/forms.tsx @@ -0,0 +1,62 @@ +export const dispatchEvent = ( + eventID: string, + element: HTMLInputElement, + options = {bubbles: true, cancelable: true}, +) => { + let event; + + if (typeof Event === 'function') { + event = new Event(eventID, options); + } else { + event = document.createEvent('Event'); + event.initEvent(eventID, options.bubbles, options.cancelable); + } + + element.dispatchEvent(event); +}; + +export const dispatchChangeEvent = (element: HTMLInputElement) => { + dispatchEvent('flood-form-change', element); +}; + +export const getDataFromForm = (form: HTMLFormElement) => + Array.from(form.elements).reduce((formData: Record, element) => { + const inputElement = element as HTMLInputElement; + const {name, type, value} = inputElement; + const retForm = formData; + + if (!name || type === 'button' || type === 'submit' || type === 'reset') { + return retForm; + } + + if (type === 'checkbox') { + retForm[name] = inputElement.checked; + } else if (type !== 'radio') { + retForm[name] = value; + } else if (type === 'radio' && !inputElement.checked && formData[name] === undefined) { + retForm[name] = null; + } else if (type === 'radio' && inputElement.checked) { + retForm[name] = value; + } + + return retForm; + }, {}); + +export const resetFormData = (form: HTMLFormElement) => + Array.from(form.elements).forEach((element) => { + const inputElement = element as HTMLInputElement; + // getAttribute is supposedly faster than using the dataset API. + const defaultValue = inputElement.getAttribute('data-initial-value'); + + if (inputElement.type === 'checkbox' || inputElement.type === 'radio') { + if (defaultValue === 'true') { + inputElement.checked = true; + } else { + inputElement.checked = false; + } + } else if (defaultValue !== null) { + inputElement.value = defaultValue; + } else { + inputElement.value = ''; + } + }); diff --git a/client/src/javascript/ui/constants/transitionTimeouts.tsx b/client/src/javascript/ui/constants/transitionTimeouts.tsx new file mode 100644 index 000000000..e5f6e7a64 --- /dev/null +++ b/client/src/javascript/ui/constants/transitionTimeouts.tsx @@ -0,0 +1,11 @@ +// Transition timeout parity with Sass variables. + +export default { + xxSlow: (7 / 8) * 1000, + xSlow: (3 / 4) * 1000, + slow: (5 / 8) * 1000, + medium: (1 / 2) * 1000, + fast: (3 / 8) * 1000, + xFast: (1 / 4) * 1000, + xxFast: (1 / 8) * 1000, +}; diff --git a/client/src/javascript/ui/icons/Checkmark.tsx b/client/src/javascript/ui/icons/Checkmark.tsx new file mode 100644 index 000000000..77908f0b9 --- /dev/null +++ b/client/src/javascript/ui/icons/Checkmark.tsx @@ -0,0 +1,10 @@ +const Checkmark = () => ( + + + +); + +export default Checkmark; diff --git a/client/src/javascript/ui/icons/Chevron.tsx b/client/src/javascript/ui/icons/Chevron.tsx new file mode 100644 index 000000000..26352ed2e --- /dev/null +++ b/client/src/javascript/ui/icons/Chevron.tsx @@ -0,0 +1,11 @@ +const Chevron = () => ( + + + +); + +export default Chevron; diff --git a/client/src/javascript/ui/icons/Circle.tsx b/client/src/javascript/ui/icons/Circle.tsx new file mode 100644 index 000000000..d253a5744 --- /dev/null +++ b/client/src/javascript/ui/icons/Circle.tsx @@ -0,0 +1,7 @@ +const Checkmark = () => ( + + + +); + +export default Checkmark; diff --git a/client/src/javascript/ui/icons/LoadingRing.tsx b/client/src/javascript/ui/icons/LoadingRing.tsx new file mode 100644 index 000000000..8424d9f54 --- /dev/null +++ b/client/src/javascript/ui/icons/LoadingRing.tsx @@ -0,0 +1,37 @@ +import classnames from 'classnames'; +import {FC} from 'react'; + +interface LoadingRingProps { + size?: string; +} + +const LoadingRing: FC = ({size}: LoadingRingProps) => { + const classes = classnames('icon icon--loading icon--loading--ring', { + 'icon--small': size === 'small', + }); + + return ( +
    +
    + + + + + +
    +
    + + + + + +
    +
    + ); +}; + +LoadingRing.defaultProps = { + size: 'small', +}; + +export default LoadingRing; diff --git a/client/src/javascript/ui/icons/Search.tsx b/client/src/javascript/ui/icons/Search.tsx new file mode 100644 index 000000000..2079d70de --- /dev/null +++ b/client/src/javascript/ui/icons/Search.tsx @@ -0,0 +1,10 @@ +const Search = () => ( + + + + + + +); + +export default Search; diff --git a/client/src/javascript/ui/index.tsx b/client/src/javascript/ui/index.tsx new file mode 100644 index 000000000..f021d93eb --- /dev/null +++ b/client/src/javascript/ui/index.tsx @@ -0,0 +1,47 @@ +import Button from './components/Button'; +import Checkbox from './components/Checkbox'; +import Container from './components/Container'; +import ContextMenu from './components/ContextMenu'; +import Form from './components/Form'; +import FormElementAddon from './components/FormElementAddon'; +import FormError from './components/FormError'; +import FormGroup from './components/FormGroup'; +import FormRow from './components/FormRow'; +import FormRowGroup from './components/FormRowGroup'; +import FormRowItem from './components/FormRowItem'; +import LoadingRing from './icons/LoadingRing'; +import Overlay from './components/Overlay'; +import Panel from './components/Panel'; +import PanelContent from './components/PanelContent'; +import PanelFooter from './components/PanelFooter'; +import PanelHeader from './components/PanelHeader'; +import Portal from './components/Portal'; +import Radio from './components/Radio'; +import Select from './components/Select'; +import SelectItem from './components/SelectItem'; +import Textbox from './components/Textbox'; +import ToggleInput from './components/ToggleInput'; + +export {Button}; +export {Checkbox}; +export {Container}; +export {ContextMenu}; +export {Form}; +export {FormElementAddon}; +export {FormError}; +export {FormGroup}; +export {FormRow}; +export {FormRowGroup}; +export {FormRowItem}; +export {LoadingRing}; +export {Overlay}; +export {Panel}; +export {PanelContent}; +export {PanelFooter}; +export {PanelHeader}; +export {Portal}; +export {Radio}; +export {Select}; +export {SelectItem}; +export {Textbox}; +export {ToggleInput}; diff --git a/client/src/javascript/util/connectStores.tsx b/client/src/javascript/util/connectStores.tsx deleted file mode 100644 index deb3dd820..000000000 --- a/client/src/javascript/util/connectStores.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React from 'react'; - -import EventTypes from '../constants/EventTypes'; - -interface GenericStore { - listen: (event: keyof typeof EventTypes, eventHandler: (payload: unknown) => void) => void; - unlisten: (event: keyof typeof EventTypes, eventHandler: (payload: unknown) => void) => void; -} - -export interface EventListenerDescriptor { - store: GenericStore; - event: (keyof typeof EventTypes) | (keyof typeof EventTypes)[]; - getValue: ( - props: { - payload: unknown; - props: WrappedComponentProps; - state: DerivedState; - store: GenericStore; - }, - ) => Partial; -} - -const connectStores = ( - InputComponent: React.JSXElementConstructor, - getEventListenerDescriptors: ( - props: WrappedComponentProps, - ) => EventListenerDescriptor[], -): ((props: WrappedComponentProps) => React.ReactElement) => { - class ConnectedComponent extends React.Component { - private eventHandlersByStore: Map< - GenericStore, - Set<{events: (keyof typeof EventTypes)[]; eventHandler: (payload: unknown) => void}> - > = new Map(); - - private constructor(props: WrappedComponentProps) { - super(props); - this.state = getEventListenerDescriptors(props).reduce( - (state, eventListenerDescriptor): DerivedState => { - const {store, getValue} = eventListenerDescriptor; - return { - ...state, - ...getValue({state, props, store, payload: null}), - }; - }, - ({} as unknown) as DerivedState, - ); - } - - public componentDidMount(): void { - const eventListenerDescriptors = getEventListenerDescriptors(this.props); - - eventListenerDescriptors.forEach( - (eventListenerDescriptor): void => { - const {store, event, getValue} = eventListenerDescriptor; - const eventHandler = (payload: unknown): void => - this.setState( - (state: DerivedState, props: WrappedComponentProps): DerivedState => - getValue({state, props, store, payload}) as DerivedState, - ); - const events = Array.isArray(event) ? event : [event]; - - events.forEach( - (storeEvent): void => { - store.listen(storeEvent, eventHandler); - }, - ); - - if (this.eventHandlersByStore.get(store) == null) { - const newSet: Set<{ - events: (keyof typeof EventTypes)[]; - eventHandler: (payload: unknown) => void; - }> = new Set(); - this.eventHandlersByStore.set(store, newSet); - } - - const eventHandlersForStore = this.eventHandlersByStore.get(store); - if (eventHandlersForStore != null) { - eventHandlersForStore.add({events, eventHandler}); - } - }, - ); - } - - public componentWillUnmount(): void { - this.eventHandlersByStore.forEach( - (listenerDescriptors, store): void => { - listenerDescriptors.forEach( - ({events, eventHandler}): void => { - events.forEach( - (event): void => { - store.unlisten(event, eventHandler); - }, - ); - }, - ); - }, - ); - - this.eventHandlersByStore.clear(); - } - - public render(): React.ReactNode { - return ; - } - } - - return (props: WrappedComponentProps): React.ReactElement => { - return ; - }; -}; - -export default connectStores; diff --git a/client/src/javascript/util/detectLocale.ts b/client/src/javascript/util/detectLocale.ts new file mode 100644 index 000000000..14bdece4a --- /dev/null +++ b/client/src/javascript/util/detectLocale.ts @@ -0,0 +1,56 @@ +import {getUserLocales} from 'get-user-locale'; +import Languages from '../constants/Languages'; + +import type {Language} from '../constants/Languages'; + +export interface LocaleConfig { + locale: string; + language: Exclude; +} + +const detectedLocales: LocaleConfig = { + locale: 'en', + language: 'en', +}; + +let localeDetected = false; + +const detectLocale = (): LocaleConfig => { + if (localeDetected) { + return detectedLocales; + } + + // Reverse loop to respect language priority of user + getUserLocales() + .reverse() + .forEach((userLocale): void => { + detectedLocales.locale = userLocale; + switch (detectedLocales.locale) { + // Special handlings for languages with variants + case 'zh': + case 'zh-CN': + case 'zh-SG': + case 'zh-MY': + detectedLocales.locale = 'zh-Hans'; + break; + case 'zh-TW': + case 'zh-HK': + case 'zh-MO': + detectedLocales.locale = 'zh-Hant'; + break; + default: + break; + } + if (Object.prototype.hasOwnProperty.call(Languages, detectedLocales.locale)) { + detectedLocales.language = detectedLocales.locale as Exclude; + } else if (Object.prototype.hasOwnProperty.call(Languages, detectedLocales.locale.substr(0, 2))) { + // In rare cases, user provides a locale (eg. en-US) without fallback (eg. en) + detectedLocales.language = detectedLocales.locale.substr(0, 2) as Exclude; + } + }); + + localeDetected = true; + return detectedLocales; +}; + +export default detectLocale; diff --git a/client/src/javascript/util/filterTorrents.js b/client/src/javascript/util/filterTorrents.js deleted file mode 100644 index 6e7e4f2a9..000000000 --- a/client/src/javascript/util/filterTorrents.js +++ /dev/null @@ -1,26 +0,0 @@ -import torrentStatusMap from '@shared/constants/torrentStatusMap'; - -export function filterTorrents(torrentList, opts) { - const {type, filter} = opts; - - if (filter !== 'all') { - if (type === 'status') { - const statusFilter = torrentStatusMap[filter]; - return torrentList.filter(torrent => torrent.status.includes(statusFilter)); - } - if (type === 'tracker') { - return torrentList.filter(torrent => torrent.trackerURIs.includes(filter)); - } - if (type === 'tag') { - return torrentList.filter(torrent => { - if (filter === 'untagged') { - return torrent.tags.length === 0; - } - - return torrent.tags.includes(filter); - }); - } - } - - return torrentList; -} diff --git a/client/src/javascript/util/filterTorrents.ts b/client/src/javascript/util/filterTorrents.ts new file mode 100644 index 000000000..18e153073 --- /dev/null +++ b/client/src/javascript/util/filterTorrents.ts @@ -0,0 +1,43 @@ +import type {TorrentProperties} from '@shared/types/Torrent'; +import type {TorrentStatus} from '@shared/constants/torrentStatusMap'; + +interface StatusFilter { + type: 'status'; + filter: TorrentStatus; +} + +interface TrackerFilter { + type: 'tracker'; + filter: string; +} + +interface TagFilter { + type: 'tag'; + filter: string; +} + +function filterTorrents(torrentList: Array, opts: StatusFilter | TrackerFilter | TagFilter) { + const {type, filter} = opts; + + if (filter !== '') { + if (type === 'status') { + return torrentList.filter((torrent) => torrent.status.includes(filter as TorrentStatus)); + } + if (type === 'tracker') { + return torrentList.filter((torrent) => torrent.trackerURIs.includes(filter)); + } + if (type === 'tag') { + return torrentList.filter((torrent) => { + if (filter === 'untagged') { + return torrent.tags.length === 0; + } + + return torrent.tags.includes(filter); + }); + } + } + + return torrentList; +} + +export default filterTorrents; diff --git a/client/src/javascript/util/history.js b/client/src/javascript/util/history.js deleted file mode 100644 index ea666d5ff..000000000 --- a/client/src/javascript/util/history.js +++ /dev/null @@ -1,8 +0,0 @@ -import {createBrowserHistory} from 'history'; - -import stringUtil from '@shared/util/stringUtil'; -import ConfigStore from '../stores/ConfigStore'; - -const history = createBrowserHistory({basename: stringUtil.withoutTrailingSlash(ConfigStore.getBaseURI())}); - -export default history; diff --git a/client/src/javascript/util/history.ts b/client/src/javascript/util/history.ts new file mode 100644 index 000000000..e92aed847 --- /dev/null +++ b/client/src/javascript/util/history.ts @@ -0,0 +1,10 @@ +import {createBrowserHistory} from 'history'; + +import stringUtil from '@shared/util/stringUtil'; +import ConfigStore from '../stores/ConfigStore'; + +const history = createBrowserHistory({ + basename: stringUtil.withoutTrailingSlash(ConfigStore.baseURI), +}); + +export default history; diff --git a/client/src/javascript/util/searchTorrents.js b/client/src/javascript/util/searchTorrents.js deleted file mode 100644 index 872a2045a..000000000 --- a/client/src/javascript/util/searchTorrents.js +++ /dev/null @@ -1,21 +0,0 @@ -export function searchTorrents(torrents, searchString) { - if (searchString !== '') { - const queries = []; - const searchTerms = searchString.replace(/,/g, ' ').split(' '); - - for (let i = 0, len = searchTerms.length; i < len; i++) { - queries.push(new RegExp(searchTerms[i], 'gi')); - } - - torrents = torrents.filter(torrent => { - for (let i = 0, len = queries.length; i < len; i++) { - if (!torrent.name.match(queries[i])) { - return false; - } - } - return true; - }); - } - - return torrents; -} diff --git a/client/src/javascript/util/searchTorrents.ts b/client/src/javascript/util/searchTorrents.ts new file mode 100644 index 000000000..f0e5325b9 --- /dev/null +++ b/client/src/javascript/util/searchTorrents.ts @@ -0,0 +1,25 @@ +import type {TorrentProperties} from '@shared/types/Torrent'; + +function searchTorrents(torrents: Array, searchString: string): Array { + if (searchString !== '') { + const queries: Array = []; + const searchTerms = searchString.replace(/,/g, ' ').split(' '); + + for (let i = 0, len = searchTerms.length; i < len; i += 1) { + queries.push(new RegExp(searchTerms[i], 'gi')); + } + + return torrents.filter((torrent) => { + for (let i = 0, len = queries.length; i < len; i += 1) { + if (!torrent.name.match(queries[i])) { + return false; + } + } + return true; + }); + } + + return torrents; +} + +export default searchTorrents; diff --git a/client/src/javascript/util/selectTorrents.js b/client/src/javascript/util/selectTorrents.js deleted file mode 100644 index 3cd72be38..000000000 --- a/client/src/javascript/util/selectTorrents.js +++ /dev/null @@ -1,65 +0,0 @@ -export function selectTorrents(options) { - if (options.event.shiftKey) { - if (options.selectedTorrents.length) { - const lastHash = options.selectedTorrents[options.selectedTorrents.length - 1]; - let currentHashIndex; - let lastHashIndex; - - // TODO: wtf? Let's just use findIndex or indexOf... - // get the index of the last selected torrent. - options.torrentList.some((torrent, index) => { - if (torrent.hash === lastHash) { - lastHashIndex = index; - return true; - } - - return false; - }); - - // TODO: wtf? Let's just use findIndex or indexOf... - // get the index of the newly selected torrent. - options.torrentList.some((torrent, index) => { - if (torrent.hash === options.hash) { - currentHashIndex = index; - return true; - } - - return false; - }); - - // from the previously selected index to the currently selected index, - // add all torrent hashes to the selected array. - // if the newly selcted hash is larger than the previous, start from - // the newly selected hash and work backwards. otherwise go forwards. - let increment = 1; - - if (currentHashIndex > lastHashIndex) { - increment = -1; - } - - while (currentHashIndex !== lastHashIndex) { - const foundHash = options.torrentList[currentHashIndex].hash; - // if the torrent isn't already selected, add the hash to the array. - if (options.selectedTorrents.indexOf(foundHash) === -1) { - options.selectedTorrents.push(foundHash); - } - currentHashIndex += increment; - } - } else { - options.selectedTorrents = [options.hash]; - } - } else if (options.event.metaKey || options.event.ctrlKey) { - const hashPosition = options.selectedTorrents.indexOf(options.hash); - if (hashPosition === -1) { - // if the hash is not in the array, add it. - options.selectedTorrents.push(options.hash); - } else { - // if the hash is in the array, remove it. - options.selectedTorrents.splice(hashPosition, 1); - } - } else { - // clicked torrent is only item in list. - options.selectedTorrents = [options.hash]; - } - return options.selectedTorrents; -} diff --git a/client/src/javascript/util/selectTorrents.ts b/client/src/javascript/util/selectTorrents.ts new file mode 100644 index 000000000..e984011c9 --- /dev/null +++ b/client/src/javascript/util/selectTorrents.ts @@ -0,0 +1,86 @@ +import * as React from 'react'; + +import type {TorrentProperties} from '@shared/types/Torrent'; + +interface SelectTorrentOptions { + event: React.MouseEvent | React.TouchEvent; + hash: string; + selectedTorrents: Array; + torrentList: Array; +} + +function selectTorrents(options: SelectTorrentOptions): string[] { + if (options.event.shiftKey) { + if (options.selectedTorrents.length) { + const lastHash = options.selectedTorrents[options.selectedTorrents.length - 1]; + let currentHashIndex = -1; + let lastHashIndex = -1; + + // TODO: wtf? Let's just use findIndex or indexOf... + // get the index of the last selected torrent. + if ( + !options.torrentList.some((torrent, index) => { + if (torrent.hash === lastHash) { + lastHashIndex = index; + return true; + } + + return false; + }) + ) { + return options.selectedTorrents; + } + + // TODO: wtf? Let's just use findIndex or indexOf... + // get the index of the newly selected torrent. + if ( + !options.torrentList.some((torrent, index) => { + if (torrent.hash === options.hash) { + currentHashIndex = index; + return true; + } + + return false; + }) + ) { + return options.selectedTorrents; + } + + // from the previously selected index to the currently selected index, + // add all torrent hashes to the selected array. + // if the newly selcted hash is larger than the previous, start from + // the newly selected hash and work backwards. otherwise go forwards. + let increment = 1; + + if (currentHashIndex > lastHashIndex) { + increment = -1; + } + + while (currentHashIndex !== lastHashIndex) { + const foundHash = options.torrentList[currentHashIndex].hash; + // if the torrent isn't already selected, add the hash to the array. + if (options.selectedTorrents.indexOf(foundHash) === -1) { + options.selectedTorrents.push(foundHash); + } + currentHashIndex += increment; + } + } else { + return [options.hash]; + } + } else if (options.event.metaKey || options.event.ctrlKey) { + const hashPosition = options.selectedTorrents.indexOf(options.hash); + if (hashPosition === -1) { + // if the hash is not in the array, add it. + options.selectedTorrents.push(options.hash); + } else { + // if the hash is in the array, remove it. + options.selectedTorrents.splice(hashPosition, 1); + } + } else { + // clicked torrent is only item in list. + return [options.hash]; + } + return options.selectedTorrents; +} + +export default selectTorrents; diff --git a/client/src/javascript/util/selectionTree.ts b/client/src/javascript/util/selectionTree.ts new file mode 100644 index 000000000..4064922f4 --- /dev/null +++ b/client/src/javascript/util/selectionTree.ts @@ -0,0 +1,165 @@ +import type {TorrentContent, TorrentContentSelection, TorrentContentSelectionTree} from '@shared/types/TorrentContent'; + +const selectAll = (tree: TorrentContentSelectionTree, isSelected: boolean): TorrentContentSelectionTree => ({ + ...tree, + ...(tree.directories != null + ? { + directories: Object.assign( + {}, + ...Object.keys(tree.directories).map((directory) => + tree.directories != null + ? { + [directory]: selectAll(tree.directories[directory], isSelected), + } + : {}, + ), + ), + } + : {}), + ...(tree.files != null + ? { + files: Object.assign( + {}, + ...Object.keys(tree.files).map((file) => + tree.files != null + ? { + [file]: { + ...tree.files[file], + isSelected, + }, + } + : {}, + ), + ), + } + : {}), + isSelected, +}); + +const applySelection = ( + tree: TorrentContentSelectionTree, + item: TorrentContentSelection, + recursiveDepth = 0, +): TorrentContentSelectionTree => { + const {depth, path, select, type} = item; + const currentPath = path[recursiveDepth]; + + // Change happens + if (recursiveDepth === depth - 1) { + if (type === 'file' && tree.files?.[currentPath] != null) { + const files = { + ...tree.files, + [currentPath]: { + ...tree.files[currentPath], + isSelected: select, + }, + }; + + return { + ...tree, + files, + isSelected: + Object.values(files).every(({isSelected}) => isSelected) && + (tree.directories != null ? Object.values(tree.directories).every(({isSelected}) => isSelected) : true), + }; + } + + if (type === 'directory' && tree.directories != null) { + const directories = { + ...tree.directories, + [currentPath]: selectAll(tree.directories[currentPath], select), + }; + + return { + ...tree, + directories, + isSelected: + Object.values(directories).every(({isSelected}) => isSelected) && + (tree.files != null ? Object.values(tree.files).every(({isSelected}) => isSelected) : true), + }; + } + + return tree; + } + + // Recursive call till we reach the target + if (tree.directories != null) { + const selectionSubTree = tree.directories; + Object.keys(selectionSubTree).forEach((directory) => { + if (directory === currentPath) { + selectionSubTree[directory] = applySelection(selectionSubTree[directory], item, recursiveDepth + 1); + } + }); + return { + ...tree, + directories: selectionSubTree, + isSelected: Object.values(selectionSubTree).every(({isSelected}) => isSelected), + }; + } + + return tree; +}; + +const getSelectedItems = (tree: TorrentContentSelectionTree) => { + const indices: Array = []; + + if (tree.files != null) { + const {files} = tree; + Object.keys(files).forEach((fileName) => { + const file = files[fileName]; + + if (file.isSelected) { + indices.push(file.index); + } + }); + } + + if (tree.directories != null) { + const {directories} = tree; + Object.keys(directories).forEach((directoryName) => { + indices.push(...getSelectedItems(directories[directoryName])); + }); + } + + return indices; +}; + +const getSelectionTree = (contents: Array, isSelected = false): TorrentContentSelectionTree => { + const tree: TorrentContentSelectionTree = {isSelected}; + + contents.forEach((content) => { + const pathComponents = content.path.split('/'); + let currentDirectory = tree; + + while (pathComponents.length - 1) { + const pathComponent = pathComponents.shift() as string; + + if (currentDirectory.directories == null) { + currentDirectory.directories = {}; + } + + if (currentDirectory.directories[pathComponent] == null) { + currentDirectory.directories[pathComponent] = {isSelected}; + } + + currentDirectory = currentDirectory.directories[pathComponent]; + } + + if (currentDirectory.files == null) { + currentDirectory.files = {}; + } + + currentDirectory.files[content.filename] = {...content, isSelected}; + }); + + return tree; +}; + +const selectionTree = { + selectAll, + applySelection, + getSelectedItems, + getSelectionTree, +}; + +export default selectionTree; diff --git a/client/src/javascript/util/size.js b/client/src/javascript/util/size.js deleted file mode 100644 index 1b8a2fb87..000000000 --- a/client/src/javascript/util/size.js +++ /dev/null @@ -1,55 +0,0 @@ -export function compute(bytes, precision = 2) { - const kilobyte = 1024; - - const megabyte = kilobyte * 1024; - - const gigabyte = megabyte * 1024; - - const terabyte = gigabyte * 1024; - let value = 0; - - let unit = ''; - - if (bytes >= terabyte) { - value = bytes / terabyte; - unit = 'TB'; - } else if (bytes >= gigabyte) { - value = bytes / gigabyte; - unit = 'GB'; - } else if (bytes >= megabyte) { - value = bytes / megabyte; - unit = 'MB'; - } else if (bytes >= kilobyte) { - value = bytes / kilobyte; - unit = 'kB'; - } else { - value = bytes; - unit = 'B'; - } - - value = Number(value); - if (!!value && value >= 100) { - value = Math.floor(value); - } else if (!!value && value > 10) { - value = Number(value.toFixed(precision - 1)); - } else if (value) { - value = Number(value.toFixed(precision)); - } - - return { - value, - unit, - }; -} - -export function getTranslationString(unit) { - const UNIT_TO_STRING_ID = { - B: 'unit.size.byte', - kB: 'unit.size.kilobyte', - MB: 'unit.size.megabyte', - GB: 'unit.size.gigabyte', - TB: 'unit.size.terabyte', - }; - - return UNIT_TO_STRING_ID[unit] || 'unit.size.byte'; -} diff --git a/client/src/javascript/util/size.ts b/client/src/javascript/util/size.ts new file mode 100644 index 000000000..881b18c66 --- /dev/null +++ b/client/src/javascript/util/size.ts @@ -0,0 +1,57 @@ +const UNIT_TO_STRING_ID = { + B: 'unit.size.byte', + kB: 'unit.size.kilobyte', + MB: 'unit.size.megabyte', + GB: 'unit.size.gigabyte', + TB: 'unit.size.terabyte', +} as const; + +type Unit = keyof typeof UNIT_TO_STRING_ID; + +export function compute(bytes: number, precision = 2): {value: number; unit: Unit} { + const kilobyte = 1024; + + const megabyte = kilobyte * 1024; + + const gigabyte = megabyte * 1024; + + const terabyte = gigabyte * 1024; + let value = 0; + + let unit: Unit; + + if (bytes >= terabyte) { + value = bytes / terabyte; + unit = 'TB'; + } else if (bytes >= gigabyte) { + value = bytes / gigabyte; + unit = 'GB'; + } else if (bytes >= megabyte) { + value = bytes / megabyte; + unit = 'MB'; + } else if (bytes >= kilobyte) { + value = bytes / kilobyte; + unit = 'kB'; + } else { + value = bytes; + unit = 'B'; + } + + value = Number(value); + if (!!value && value >= 100) { + value = Math.floor(value); + } else if (!!value && value > 10) { + value = Number(value.toFixed(precision - 1)); + } else if (value) { + value = Number(value.toFixed(precision)); + } + + return { + value, + unit, + }; +} + +export function getTranslationString(unit: Unit): typeof UNIT_TO_STRING_ID[Unit] { + return UNIT_TO_STRING_ID[unit] || UNIT_TO_STRING_ID.B; +} diff --git a/client/src/javascript/util/sortTorrents.js b/client/src/javascript/util/sortTorrents.js deleted file mode 100644 index 30d9599ee..000000000 --- a/client/src/javascript/util/sortTorrents.js +++ /dev/null @@ -1,78 +0,0 @@ -// TODO: Split up this garbage. - -const stringProps = ['basePath', 'comment', 'hash', 'message', 'name']; - -export function sortTorrents(torrentsHash, sortBy) { - const torrents = Object.keys(torrentsHash).map(hash => ({hash, ...torrentsHash[hash]})); - - if (torrents.length) { - const {direction, property} = sortBy; - - torrents.sort((a, b) => { - let valA = a[property]; - let valB = b[property]; - - if (property === 'peers' || property === 'seeds') { - valA = a[`${property}Connected`]; - valB = b[`${property}Connected`]; - - if (valA === valB) { - valA = a[`${property}Total`]; - valB = b[`${property}Total`]; - } - } else if (property === 'eta') { - // Keep Infinity and null values at bottom of array. - if ((valA === 'Infinity' && valB !== 'Infinity') || (valA == null && valB != null)) { - return 1; - } - if ((valA !== 'Infinity' && valB === 'Infinity') || (valA != null && valB == null)) { - return -1; - } - if (valA == null && valB == null) { - return 0; - } - - // If it's not infinity, compare the cumulative seconds as regular numbers. - if (valA !== 'Infinity') { - valA = Number(valA.cumSeconds); - } - - if (valB !== 'Infinity') { - valB = Number(valB.cumSeconds); - } - } else if (property === 'tags') { - // TODO: Find a better way to sort tags. - valA = valA.join(',').toLowerCase(); - valB = valB.join(',').toLowerCase(); - } else if (stringProps.includes(property)) { - valA = valA.toLowerCase(); - valB = valB.toLowerCase(); - } else { - valA = Number(valA); - valB = Number(valB); - } - - // TODO: Use locale compare for sorting strings. - if (direction === 'asc') { - if (valA > valB) { - return 1; - } - if (valA < valB) { - return -1; - } - } else { - if (valA > valB) { - return -1; - } - if (valA < valB) { - return 1; - } - } - - return 0; - }); - - return torrents; - } - return torrents; -} diff --git a/client/src/javascript/util/sortTorrents.ts b/client/src/javascript/util/sortTorrents.ts new file mode 100644 index 000000000..8eeff9d9d --- /dev/null +++ b/client/src/javascript/util/sortTorrents.ts @@ -0,0 +1,56 @@ +import sort from 'fast-sort'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; +import type {TorrentProperties} from '@shared/types/Torrent'; + +type SortRule = { + [direction in FloodSettings['sortTorrents']['direction']]: + | keyof TorrentProperties + | ((p: TorrentProperties) => unknown); +}; + +function sortTorrents(torrents: Array, sortBy: Readonly) { + const {property} = sortBy; + const sortRules: Array = []; + + switch (property) { + case 'peers': + case 'seeds': + sortRules.push( + {[sortBy.direction]: `${property}Connected`} as SortRule, + {[sortBy.direction]: `${property}Total`} as SortRule, + ); + break; + case 'eta': + sortRules.push({ + [sortBy.direction]: (p: TorrentProperties) => { + if (p.eta === -1) { + return Infinity; + } + return p.eta; + }, + } as SortRule); + break; + case 'tags': + sortRules.push({ + [sortBy.direction]: (p: TorrentProperties) => p[property].join(',').toLowerCase(), + } as SortRule); + break; + case 'directory': + case 'hash': + case 'message': + case 'name': + // Those fields are strings. We want case-insensitive sorting. + sortRules.push({ + [sortBy.direction]: (p: TorrentProperties) => p[property].toLowerCase(), + } as SortRule); + break; + default: + sortRules.push({[sortBy.direction]: property} as SortRule); + break; + } + + return sort(torrents).by(sortRules); +} + +export default sortTorrents; diff --git a/client/src/javascript/util/torrentListCellContents.tsx b/client/src/javascript/util/torrentListCellContents.tsx new file mode 100644 index 000000000..b5234602a --- /dev/null +++ b/client/src/javascript/util/torrentListCellContents.tsx @@ -0,0 +1,87 @@ +import {FormattedDate, FormattedMessage, FormattedNumber} from 'react-intl'; + +import type {TorrentProperties} from '@shared/types/Torrent'; + +import Checkmark from '../components/icons/Checkmark'; +import Duration from '../components/general/Duration'; +import Size from '../components/general/Size'; + +import type {TorrentListColumn} from '../constants/TorrentListColumns'; + +const booleanTransformer = (value: boolean) => + value ? : null; +const dateTransformer = (date: number) => ; +const peersTransformer = (peersConnected: number, totalPeers: number) => ( + , + of: ( + + + + ), + total: , + }} + /> +); +const speedTransformer = (value: number) => ; +const sizeTransformer = (value: number) => ; + +export const torrentListCellTransformers = { + dateAdded: dateTransformer, + dateCreated: dateTransformer, + downRate: speedTransformer, + downTotal: sizeTransformer, + isPrivate: booleanTransformer, + peers: peersTransformer, + seeds: peersTransformer, + tags: (tags: TorrentProperties['tags']) => ( +
      + {tags.map((tag) => ( +
    • + {tag} +
    • + ))} +
    + ), + ratio: (ratio: TorrentProperties['ratio']) => , + sizeBytes: sizeTransformer, + trackerURIs: (trackers: TorrentProperties['trackerURIs']) => trackers.join(', '), + upRate: speedTransformer, + upTotal: sizeTransformer, + eta: (eta: TorrentProperties['eta']) => { + if (!eta) { + return null; + } + + return ; + }, +}; + +export const getTorrentListCellContent = (torrent: TorrentProperties, column: TorrentListColumn) => { + switch (column) { + case 'dateAdded': + case 'dateCreated': + case 'downRate': + case 'upRate': + case 'downTotal': + case 'upTotal': + case 'sizeBytes': + case 'ratio': + return torrentListCellTransformers[column](torrent[column]); + case 'isPrivate': + return torrentListCellTransformers[column](torrent[column]); + case 'tags': + case 'trackerURIs': + return torrentListCellTransformers[column](torrent[column]); + case 'eta': + return torrentListCellTransformers[column](torrent[column]); + case 'seeds': + return torrentListCellTransformers[column](torrent.seedsConnected, torrent.seedsTotal); + case 'peers': + return torrentListCellTransformers[column](torrent.peersConnected, torrent.peersTotal); + default: + return torrent[column]; + } +}; diff --git a/client/src/javascript/util/torrentPropertyIcons.tsx b/client/src/javascript/util/torrentPropertyIcons.tsx new file mode 100644 index 000000000..0d3ae7d54 --- /dev/null +++ b/client/src/javascript/util/torrentPropertyIcons.tsx @@ -0,0 +1,37 @@ +import CalendarCreatedIcon from '../components/icons/CalendarCreatedIcon'; +import CalendarIcon from '../components/icons/CalendarIcon'; +import ClockIcon from '../components/icons/ClockIcon'; +import DiskIcon from '../components/icons/DiskIcon'; +import DownloadThickIcon from '../components/icons/DownloadThickIcon'; +import HashIcon from '../components/icons/HashIcon'; +import FolderClosedSolid from '../components/icons/FolderClosedSolid'; +import PeersIcon from '../components/icons/PeersIcon'; +import LockIcon from '../components/icons/LockIcon'; +import RadarIcon from '../components/icons/RadarIcon'; +import RatioIcon from '../components/icons/RatioIcon'; +import SeedsIcon from '../components/icons/SeedsIcon'; +import TrackerMessageIcon from '../components/icons/TrackerMessageIcon'; +import UploadThickIcon from '../components/icons/UploadThickIcon'; + +import type {TorrentListColumn} from '../constants/TorrentListColumns'; + +const ICONS: Partial> = { + eta: , + sizeBytes: , + downRate: , + directory: , + hash: , + dateAdded: , + dateCreated: , + isPrivate: , + message: , + percentComplete: , + peers: , + ratio: , + seeds: , + trackerURIs: , + upRate: , + upTotal: , +} as const; + +export default ICONS; diff --git a/client/src/javascript/util/torrentStatusClasses.js b/client/src/javascript/util/torrentStatusClasses.js deleted file mode 100644 index 1a72dbe5b..000000000 --- a/client/src/javascript/util/torrentStatusClasses.js +++ /dev/null @@ -1,16 +0,0 @@ -import classnames from 'classnames'; -import torrentStatusMap from '@shared/constants/torrentStatusMap'; - -export function torrentStatusClasses(torrent, ...classes) { - return classnames(classes, { - 'torrent--has-error': torrent.status.includes(torrentStatusMap.error), - 'torrent--is-stopped': torrent.status.includes(torrentStatusMap.stopped), - 'torrent--is-downloading': torrent.status.includes(torrentStatusMap.downloading), - 'torrent--is-downloading--actively': torrent.status.includes(torrentStatusMap.activelyDownloading), - 'torrent--is-uploading--actively': torrent.status.includes(torrentStatusMap.activelyUploading), - 'torrent--is-seeding': torrent.status.includes(torrentStatusMap.seeding), - 'torrent--is-completed': torrent.status.includes(torrentStatusMap.complete), - 'torrent--is-checking': torrent.status.includes(torrentStatusMap.checking), - 'torrent--is-inactive': torrent.status.includes(torrentStatusMap.inactive), - }); -} diff --git a/client/src/javascript/util/torrentStatusClasses.ts b/client/src/javascript/util/torrentStatusClasses.ts new file mode 100644 index 000000000..1804087fc --- /dev/null +++ b/client/src/javascript/util/torrentStatusClasses.ts @@ -0,0 +1,22 @@ +import classnames from 'classnames'; + +import type {TorrentProperties} from '@shared/types/Torrent'; + +function torrentStatusClasses( + {status, downRate, upRate}: Pick, + ...classes: Array +) { + return classnames(classes, { + 'torrent--has-error': status.includes('error'), + 'torrent--is-stopped': status.includes('stopped'), + 'torrent--is-downloading': status.includes('downloading'), + 'torrent--is-downloading--actively': downRate > 0, + 'torrent--is-uploading--actively': upRate > 0, + 'torrent--is-seeding': status.includes('seeding'), + 'torrent--is-completed': status.includes('complete'), + 'torrent--is-checking': status.includes('checking'), + 'torrent--is-inactive': status.includes('inactive'), + }); +} + +export default torrentStatusClasses; diff --git a/client/src/javascript/util/torrentStatusIcons.js b/client/src/javascript/util/torrentStatusIcons.js deleted file mode 100644 index c5f67285e..000000000 --- a/client/src/javascript/util/torrentStatusIcons.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import torrentStatusMap from '@shared/constants/torrentStatusMap'; - -import ErrorIcon from '../components/icons/ErrorIcon'; -import SpinnerIcon from '../components/icons/SpinnerIcon'; -import StartIcon from '../components/icons/StartIcon'; -import StopIcon from '../components/icons/StopIcon'; - -const STATUS_ICON_MAP = { - error: , - hashChecking: , - stopped: , - running: , -}; - -export function torrentStatusIcons(status) { - let statusString; - const statusConditions = { - hashChecking: [status.includes(torrentStatusMap.checking)], - error: [status.includes(torrentStatusMap.error)], - stopped: [status.includes(torrentStatusMap.stopped)], - running: [status.includes(torrentStatusMap.downloading), status.includes(torrentStatusMap.seeding)], - }; - - Object.keys(statusConditions).some(conditionName => { - const conditions = statusConditions[conditionName]; - - conditions.some(condition => { - if (condition) { - statusString = conditionName; - } - - return condition; - }); - - return statusString != null; - }); - - return STATUS_ICON_MAP[statusString]; -} diff --git a/client/src/javascript/util/torrentStatusIcons.tsx b/client/src/javascript/util/torrentStatusIcons.tsx new file mode 100644 index 000000000..4d80dfb43 --- /dev/null +++ b/client/src/javascript/util/torrentStatusIcons.tsx @@ -0,0 +1,28 @@ +import type {TorrentStatus} from '@shared/constants/torrentStatusMap'; + +import ErrorIcon from '../components/icons/ErrorIcon'; +import SpinnerIcon from '../components/icons/SpinnerIcon'; +import StartIcon from '../components/icons/StartIcon'; +import StopIcon from '../components/icons/StopIcon'; + +const STATUS_ICON_MAP: Partial> = { + error: , + checking: , + stopped: , + downloading: , + seeding: , +} as const; + +function torrentStatusIcons(statuses: Array) { + let resultIcon: JSX.Element = ; + Object.entries(STATUS_ICON_MAP).some(([status, icon]) => { + if (statuses.includes(status as TorrentStatus) && icon != null) { + resultIcon = icon; + return true; + } + return false; + }); + return resultIcon; +} + +export default torrentStatusIcons; diff --git a/client/src/javascript/util/userPreferences.ts b/client/src/javascript/util/userPreferences.ts new file mode 100644 index 000000000..e4bc254b4 --- /dev/null +++ b/client/src/javascript/util/userPreferences.ts @@ -0,0 +1,47 @@ +import type {FloodSettings} from '@shared/types/FloodSettings'; +import type {TorrentProperties} from '@shared/types/Torrent'; + +import SettingActions from '../actions/SettingActions'; +import SettingStore from '../stores/SettingStore'; + +export const saveAddTorrentsUserPreferences = ({ + start, + destination, + tags, + tab, +}: { + start?: FloodSettings['startTorrentsOnLoad']; + destination?: string; + tags?: TorrentProperties['tags']; + tab?: FloodSettings['UITorrentsAddTab']; +}) => { + const changedSettings: Partial = {}; + + if (start != null) { + changedSettings.startTorrentsOnLoad = start; + } + + if (destination != null && destination !== '') { + if (changedSettings.torrentDestinations == null) { + changedSettings.torrentDestinations = SettingStore.floodSettings.torrentDestinations || {}; + } + + if (typeof tags?.[0] === 'string') { + changedSettings.torrentDestinations[tags[0]] = destination; + } else { + changedSettings.torrentDestinations[''] = destination; + } + } + + if (tab != null) { + changedSettings.UITorrentsAddTab = tab; + } + + SettingActions.saveSettings(changedSettings); +}; + +export const saveDeleteTorrentsUserPreferences = ({deleteData}: {deleteData?: boolean}) => { + if (deleteData != null) { + SettingActions.saveSetting('deleteTorrentData', deleteData); + } +}; diff --git a/client/src/javascript/util/validators.js b/client/src/javascript/util/validators.js deleted file mode 100644 index a34175181..000000000 --- a/client/src/javascript/util/validators.js +++ /dev/null @@ -1,28 +0,0 @@ -import regEx from '@shared/util/regEx'; - -export const isNotEmpty = value => { - return value != null && value !== ''; -}; - -export const isRegExValid = regExToCheck => { - try { - // eslint-disable-next-line no-new - new RegExp(regExToCheck); - } catch (err) { - return false; - } - - return true; -}; - -export const isURLValid = url => { - return url != null && url !== '' && url.match(regEx.url) !== null; -}; - -export const isPositiveInteger = value => { - if (value === null || value === '') return false; - - const number = parseInt(value, 10); - - return !Number.isNaN(number) && number > 0; -}; diff --git a/client/src/javascript/util/validators.ts b/client/src/javascript/util/validators.ts new file mode 100644 index 000000000..dccb82d9f --- /dev/null +++ b/client/src/javascript/util/validators.ts @@ -0,0 +1,24 @@ +import {url as matchURL} from '@shared/util/regEx'; + +export const isNotEmpty = (value: string) => value != null && value !== ''; + +export const isRegExValid = (regExToCheck: string) => { + try { + // eslint-disable-next-line no-new + new RegExp(regExToCheck); + } catch (err) { + return false; + } + + return true; +}; + +export const isURLValid = (url: string) => url != null && url !== '' && url.match(matchURL) !== null; + +export const isPositiveInteger = (value: number | string) => { + if (value === null || value === '') return false; + + const number = parseInt(`${value}`, 10); + + return !Number.isNaN(number) && number > 0; +}; diff --git a/client/src/public/android-chrome-192x192.png b/client/src/public/android-chrome-192x192.png deleted file mode 100644 index ff7a4ebb0..000000000 Binary files a/client/src/public/android-chrome-192x192.png and /dev/null differ diff --git a/client/src/public/android-chrome-512x512.png b/client/src/public/android-chrome-512x512.png deleted file mode 100644 index f340805ae..000000000 Binary files a/client/src/public/android-chrome-512x512.png and /dev/null differ diff --git a/client/src/public/apple-touch-icon.png b/client/src/public/apple-touch-icon.png deleted file mode 100644 index 814f25e55..000000000 Binary files a/client/src/public/apple-touch-icon.png and /dev/null differ diff --git a/client/src/public/browserconfig.xml b/client/src/public/browserconfig.xml deleted file mode 100644 index f80e21aa2..000000000 --- a/client/src/public/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - #0e2337 - - - diff --git a/client/src/public/favicon-16x16.png b/client/src/public/favicon-16x16.png deleted file mode 100644 index 8c5148a4e..000000000 Binary files a/client/src/public/favicon-16x16.png and /dev/null differ diff --git a/client/src/public/favicon-32x32.png b/client/src/public/favicon-32x32.png deleted file mode 100644 index 4f629c305..000000000 Binary files a/client/src/public/favicon-32x32.png and /dev/null differ diff --git a/client/src/public/safari-pinned-tab.svg b/client/src/public/icon.svg similarity index 100% rename from client/src/public/safari-pinned-tab.svg rename to client/src/public/icon.svg diff --git a/client/src/public/icon_128x128.png b/client/src/public/icon_128x128.png new file mode 100644 index 000000000..9d1544f21 Binary files /dev/null and b/client/src/public/icon_128x128.png differ diff --git a/client/src/public/icon_192x192.png b/client/src/public/icon_192x192.png new file mode 100644 index 000000000..9f339d0f5 Binary files /dev/null and b/client/src/public/icon_192x192.png differ diff --git a/client/src/public/icon_256x256.png b/client/src/public/icon_256x256.png new file mode 100644 index 000000000..8a9df4955 Binary files /dev/null and b/client/src/public/icon_256x256.png differ diff --git a/client/src/public/icon_384x384.png b/client/src/public/icon_384x384.png new file mode 100644 index 000000000..62cad04a3 Binary files /dev/null and b/client/src/public/icon_384x384.png differ diff --git a/client/src/public/icon_512x512.png b/client/src/public/icon_512x512.png new file mode 100644 index 000000000..5b8938dca Binary files /dev/null and b/client/src/public/icon_512x512.png differ diff --git a/client/src/public/icon_96x96.png b/client/src/public/icon_96x96.png new file mode 100644 index 000000000..513182471 Binary files /dev/null and b/client/src/public/icon_96x96.png differ diff --git a/client/src/public/icon_maskable.png b/client/src/public/icon_maskable.png new file mode 100644 index 000000000..ba794e687 Binary files /dev/null and b/client/src/public/icon_maskable.png differ diff --git a/client/src/public/icon_maskable_180x180.png b/client/src/public/icon_maskable_180x180.png new file mode 100644 index 000000000..c54d163aa Binary files /dev/null and b/client/src/public/icon_maskable_180x180.png differ diff --git a/client/src/public/manifest.json b/client/src/public/manifest.json index ebde2b4e5..c31b0f31d 100644 --- a/client/src/public/manifest.json +++ b/client/src/public/manifest.json @@ -1,19 +1,47 @@ { - "name": "Flood", "icons": [ { - "src": "android-chrome-192x192.png", + "src": "icon_maskable.png", + "sizes": "1024x1024", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icon_512x512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icon_384x384.png", + "sizes": "384x384", + "type": "image/png" + }, + { + "src": "icon_256x256.png", + "sizes": "256x256", + "type": "image/png" + }, + { + "src": "icon_192x192.png", "sizes": "192x192", "type": "image/png" }, { - "src": "android-chrome-512x512.png", - "sizes": "512x512", + "src": "icon_128x128.png", + "sizes": "128x128", + "type": "image/png" + }, + { + "src": "icon_96x96.png", + "sizes": "96x96", "type": "image/png" } ], + "name": "Flood", + "short_name": "Flood", + "display": "standalone", "start_url": "./index.html", - "theme_color": "#ffffff", - "background_color": "#1d2938", - "display": "standalone" + "description": "Web UI for torrent clients", + "background_color": "#ffffff", + "theme_color": "#349cf4" } diff --git a/client/src/public/mstile-144x144.png b/client/src/public/mstile-144x144.png deleted file mode 100644 index fe7a7c2a1..000000000 Binary files a/client/src/public/mstile-144x144.png and /dev/null differ diff --git a/client/src/public/mstile-150x150.png b/client/src/public/mstile-150x150.png deleted file mode 100644 index fec5de2c4..000000000 Binary files a/client/src/public/mstile-150x150.png and /dev/null differ diff --git a/client/src/public/mstile-310x150.png b/client/src/public/mstile-310x150.png deleted file mode 100644 index 79c406cb1..000000000 Binary files a/client/src/public/mstile-310x150.png and /dev/null differ diff --git a/client/src/public/mstile-310x310.png b/client/src/public/mstile-310x310.png deleted file mode 100644 index 373bc4069..000000000 Binary files a/client/src/public/mstile-310x310.png and /dev/null differ diff --git a/client/src/public/mstile-70x70.png b/client/src/public/mstile-70x70.png deleted file mode 100644 index 069493a0b..000000000 Binary files a/client/src/public/mstile-70x70.png and /dev/null differ diff --git a/client/src/sass/base/_animations.scss b/client/src/sass/base/_animations.scss index fa3e6d6ab..5cfd80c82 100644 --- a/client/src/sass/base/_animations.scss +++ b/client/src/sass/base/_animations.scss @@ -1,5 +1,4 @@ @keyframes fade-in { - 0% { opacity: 0; } @@ -7,11 +6,9 @@ 100% { opacity: 1; } - } @keyframes fade-out { - 0% { opacity: 1; } @@ -19,5 +16,4 @@ 100% { opacity: 0; } - } diff --git a/client/src/sass/base/_font-families.scss b/client/src/sass/base/_font-families.scss index 385d7aa1b..4039cb09a 100644 --- a/client/src/sass/base/_font-families.scss +++ b/client/src/sass/base/_font-families.scss @@ -2,68 +2,33 @@ font-family: 'Roboto'; font-weight: 400; font-style: normal; - src: url('../fonts/Roboto-regular/Roboto-regular.eot'); - src: url('../fonts/Roboto-regular/Roboto-regular.eot?#iefix') format('embedded-opentype'), - local('Roboto'), - local('Roboto-regular'), - url('../fonts/Roboto-regular/Roboto-regular.woff2') format('woff2'), - url('../fonts/Roboto-regular/Roboto-regular.woff') format('woff'), - url('../fonts/Roboto-regular/Roboto-regular.ttf') format('truetype'), - url('../fonts/Roboto-regular/Roboto-regular.svg#Roboto') format('svg'); + src: local('Roboto-regular'), url('../fonts/Roboto-regular/Roboto-regular.woff2') format('woff2'); } @font-face { font-family: 'Roboto'; font-weight: 500; font-style: normal; - src: url('../fonts/Roboto-500/Roboto-500.eot'); - src: url('../fonts/Roboto-500/Roboto-500.eot?#iefix') format('embedded-opentype'), - local('Roboto Medium'), - local('Roboto-500'), - url('../fonts/Roboto-500/Roboto-500.woff2') format('woff2'), - url('../fonts/Roboto-500/Roboto-500.woff') format('woff'), - url('../fonts/Roboto-500/Roboto-500.ttf') format('truetype'), - url('../fonts/Roboto-500/Roboto-500.svg#Roboto') format('svg'); + src: local('Roboto-500'), url('../fonts/Roboto-500/Roboto-500.woff2') format('woff2'); } @font-face { font-family: 'Roboto'; font-weight: 700; font-style: normal; - src: url('../fonts/Roboto-700/Roboto-700.eot'); - src: url('../fonts/Roboto-700/Roboto-700.eot?#iefix') format('embedded-opentype'), - local('Roboto Bold'), - local('Roboto-700'), - url('../fonts/Roboto-700/Roboto-700.woff2') format('woff2'), - url('../fonts/Roboto-700/Roboto-700.woff') format('woff'), - url('../fonts/Roboto-700/Roboto-700.ttf') format('truetype'), - url('../fonts/Roboto-700/Roboto-700.svg#Roboto') format('svg'); + src: local('Roboto-700'), url('../fonts/Roboto-700/Roboto-700.woff2') format('woff2'); } @font-face { font-family: 'Roboto'; font-weight: 400; font-style: italic; - src: url('../fonts/Roboto-italic/Roboto-italic.eot'); - src: url('../fonts/Roboto-italic/Roboto-italic.eot?#iefix') format('embedded-opentype'), - local('Roboto Italic'), - local('Roboto-italic'), - url('../fonts/Roboto-italic/Roboto-italic.woff2') format('woff2'), - url('../fonts/Roboto-italic/Roboto-italic.woff') format('woff'), - url('../fonts/Roboto-italic/Roboto-italic.ttf') format('truetype'), - url('../fonts/Roboto-italic/Roboto-italic.svg#Roboto') format('svg'); + src: local('Roboto-italic'), url('../fonts/Roboto-italic/Roboto-italic.woff2') format('woff2'); } @font-face { font-family: 'Roboto'; font-weight: 700; font-style: italic; - src: url('../fonts/Roboto-700italic/Roboto-700italic.eot'); - src: url('../fonts/Roboto-700italic/Roboto-700italic.eot?#iefix') format('embedded-opentype'), - local('Roboto Bold Italic'), - local('Roboto-700italic'), - url('../fonts/Roboto-700italic/Roboto-700italic.woff2') format('woff2'), - url('../fonts/Roboto-700italic/Roboto-700italic.woff') format('woff'), - url('../fonts/Roboto-700italic/Roboto-700italic.ttf') format('truetype'), - url('../fonts/Roboto-700italic/Roboto-700italic.svg#Roboto') format('svg'); + src: local('Roboto Bold Italic'), url('../fonts/Roboto-700italic/Roboto-700italic.woff2') format('woff2'); } diff --git a/client/src/sass/base/_layout.scss b/client/src/sass/base/_layout.scss index b16ddaf8d..803f2c60c 100644 --- a/client/src/sass/base/_layout.scss +++ b/client/src/sass/base/_layout.scss @@ -17,7 +17,6 @@ body { } .application { - &, &__view { align-content: center; @@ -27,6 +26,40 @@ body { justify-content: center; height: 100%; width: 100%; + + &--sidebar-alternative-state { + .application__sidebar { + display: none; + } + + @media (max-width: 720px) { + .application__sidebar { + display: block; + transform: translateX(120px); + z-index: unset; + } + + .application__content { + transform: translateX(120px); + + .modal { + transform: translateX(-240px); + } + + .context-menu__items { + transform: translateX(-240px); + } + + .alerts__list { + transform: translateX(-240px); + } + + .table__heading__resize-line { + left: -240px; + } + } + } + } } &__content { @@ -37,6 +70,13 @@ body { height: 100%; justify-content: center; position: relative; + min-width: 60px; + transition: transform 0.2s; + + @media (max-width: 720px) { + width: 100vw; + transform: translateX(-120px); + } } &__panel { diff --git a/client/src/sass/base/_typography.scss b/client/src/sass/base/_typography.scss index dfcc79521..40e3576b9 100644 --- a/client/src/sass/base/_typography.scss +++ b/client/src/sass/base/_typography.scss @@ -7,7 +7,6 @@ body { color: $foreground; font-family: $font-family; font-size: $font-size--medium; - font-smoothing: antialiased; font-weight: 400; line-height: 1.25; text-rendering: optimizeLegibility; @@ -24,7 +23,7 @@ h1, h2, h3, h4, -h5 +h5, h6, .h1, .h2, @@ -104,10 +103,9 @@ p { } .copy { - &--lead { font-size: $font-size--medium; line-height: 1.5rem; font-weight: 300; } -} \ No newline at end of file +} diff --git a/client/src/sass/components/_action-bar.scss b/client/src/sass/components/_action-bar.scss index ed4fecba2..eabf5158a 100644 --- a/client/src/sass/components/_action-bar.scss +++ b/client/src/sass/components/_action-bar.scss @@ -2,7 +2,6 @@ $action-bar--background: transparent; $action-bar--foreground: #1b1a1c; $action-bar--group--border: rgba(#7a8080, 0.15); -$action--foreground: #8899a8; $action--foreground--hover: $blue; $action--background--hover: rgba(#333e4a, 0.05); $action--border--hover: rgba(#333e4a, 0.15); @@ -14,17 +13,21 @@ $torrent-list--border: rgba($sidebar--background, 0.15); border-bottom: 1px solid $torrent-list--border; color: $action-bar--foreground; display: flex; - flex: 0 0 60px; height: 60px; position: relative; + -webkit-user-select: none; + -webkit-touch-callout: none; + + @media (max-width: 515px) { + height: 45px; + } + &--is-condensed { - flex-basis: 30px; height: 30px; } &__item { - &:first-child { text-align: left; } @@ -35,10 +38,10 @@ $torrent-list--border: rgba($sidebar--background, 0.15); &--sort-torrents { flex: 1 0 auto; + display: flex; + align-items: center; .dropdown { - margin: 5px 0 0 15px; - &__content { min-width: 250px; } @@ -59,6 +62,10 @@ $torrent-list--border: rgba($sidebar--background, 0.15); display: flex; height: 60px; + @media (max-width: 515px) { + height: 45px; + } + &__content { min-width: 430px; right: 4px; @@ -72,6 +79,10 @@ $torrent-list--border: rgba($sidebar--background, 0.15); font-size: 0; padding: 0 15px; + @media (max-width: 515px) { + padding: 0 10px; + } + .action-bar--is-condensed & { padding: 0 10px; } @@ -97,9 +108,7 @@ $torrent-list--border: rgba($sidebar--background, 0.15); } .action { - box-shadow: - 1px 0 transparent, - -1px 0 transparent; + box-shadow: 1px 0 transparent, -1px 0 transparent; cursor: pointer; display: inline-block; height: 60px; @@ -110,15 +119,23 @@ $torrent-list--border: rgba($sidebar--background, 0.15); &:hover { background: $action--background--hover; - box-shadow: - 1px 0 $action--border--hover, - -1px 0 $action--border--hover; + box-shadow: 1px 0 $action--border--hover, -1px 0 $action--border--hover; .icon { fill: $action--foreground--hover; } } + @media (max-width: 515px) { + height: 45px; + width: 46px; + + .icon { + height: 18px; + width: 18px; + } + } + .action-bar--is-condensed & { height: 30px; width: 46px; @@ -137,7 +154,7 @@ $torrent-list--border: rgba($sidebar--background, 0.15); } .icon { - fill: $action--foreground; + @include theme('fill', 'action--foreground'); height: 25px; left: 50%; position: absolute; diff --git a/client/src/sass/components/_alerts.scss b/client/src/sass/components/_alerts.scss index b7c7066bf..bf2347d55 100644 --- a/client/src/sass/components/_alerts.scss +++ b/client/src/sass/components/_alerts.scss @@ -15,6 +15,9 @@ $alert--foreground: #8fa2b2; width: 250px; z-index: 99; + -webkit-user-select: none; + -webkit-touch-callout: none; + &-exit { opacity: 1; diff --git a/client/src/sass/components/_badge.scss b/client/src/sass/components/_badge.scss index e10863c6e..a96d40668 100644 --- a/client/src/sass/components/_badge.scss +++ b/client/src/sass/components/_badge.scss @@ -1,11 +1,7 @@ -$sidebar-filter--count--foreground: darken($grey, 5%); -$sidebar-filter--count--background: lighten($grey, 20%); -$sidebar-filter--count--background--active: $blue; - .badge { - background: $sidebar-filter--count--background; + @include theme('background', 'sidebar-filter--count--background'); border-radius: 100px; - color: $sidebar-filter--count--foreground; + @include theme('color', 'sidebar-filter--count--foreground'); display: inline-block; font-size: 0.75rem; font-weight: 700; diff --git a/client/src/sass/components/_client-stats.scss b/client/src/sass/components/_client-stats.scss index 1933deabd..b69bda393 100644 --- a/client/src/sass/components/_client-stats.scss +++ b/client/src/sass/components/_client-stats.scss @@ -5,11 +5,11 @@ $client-stats--speed--stroke--width: 2px; $client-stats--download--primary--foreground: #39ce83; $client-stats--download--secondary--foreground: rgba($client-stats--download--primary--foreground, 0.75); -$client-stats--download--graph--stroke: #39Ce83; +$client-stats--download--graph--stroke: #39ce83; $client-stats--download--graph--fill--top: #2bae6c; $client-stats--download--graph--fill--bottom: #2bae6c; -$client-stats--upload--primary--foreground: #349Cf4; +$client-stats--upload--primary--foreground: #349cf4; $client-stats--upload--secondary--foreground: rgba($client-stats--upload--primary--foreground, 0.75); $client-stats--upload--graph--stroke: #349cf4; $client-stats--upload--graph--fill--top: #2387d9; @@ -40,11 +40,8 @@ $client-stats--timestamp--foreground: #526780; color: $client-stats--download--primary--foreground; .client-stats { - &__rate { - &__data { - &--limit { background: $client-stats--download--primary--foreground; } @@ -61,11 +58,8 @@ $client-stats--timestamp--foreground: #526780; color: $client-stats--upload--primary--foreground; .client-stats { - &__rate { - &__data { - &--limit { background: $client-stats--upload--primary--foreground; } @@ -91,6 +85,7 @@ $client-stats--timestamp--foreground: #526780; } &__data { + user-select: none; &--secondary, &--timestamp { @@ -130,7 +125,7 @@ $client-stats--timestamp--foreground: #526780; &--limit { align-items: center; display: inline-flex; - color: $sidebar--background; + @include theme('color', 'sidebar--background'); border-radius: 2px; font-size: 0.8em; font-style: normal; @@ -178,9 +173,7 @@ $client-stats--timestamp--foreground: #526780; } .graph { - &__gradient { - &--bottom { stop-opacity: $client-stats--speed--fill--bottom--opacity; diff --git a/client/src/sass/components/_connection-status.scss b/client/src/sass/components/_connection-status.scss index c48dbb30a..1247a98fa 100644 --- a/client/src/sass/components/_connection-status.scss +++ b/client/src/sass/components/_connection-status.scss @@ -10,7 +10,7 @@ } &__copy { - color: $light-grey--lighter; + color: $dark-grey--light; font-size: 14px; } -} \ No newline at end of file +} diff --git a/client/src/sass/components/_dependency-list.scss b/client/src/sass/components/_dependency-list.scss index f846bc477..82d5ea21d 100644 --- a/client/src/sass/components/_dependency-list.scss +++ b/client/src/sass/components/_dependency-list.scss @@ -28,9 +28,7 @@ opacity: 1; .dependency-list { - &__dependency { - &__icon { opacity: 1; } diff --git a/client/src/sass/components/_directory-tree.scss b/client/src/sass/components/_directory-tree.scss index 06ba74688..0d85dc433 100644 --- a/client/src/sass/components/_directory-tree.scss +++ b/client/src/sass/components/_directory-tree.scss @@ -14,18 +14,14 @@ $selection-toolbar--height: $spacing-unit * 1.5; min-height: 0; &--toolbar-visible { - .directory-tree { - &__selection-toolbar { transform: translateY(0); } } .modal__content { - &--nested-scroll { - &__content { margin-bottom: $selection-toolbar--height; } @@ -70,16 +66,13 @@ $selection-toolbar--height: $spacing-unit * 1.5; } .dropdown { - &__items { font-size: 1.1em; padding-bottom: 0; } &__trigger { - .dropdown { - &__button { padding-top: 5px; padding-bottom: 5px; @@ -99,7 +92,6 @@ $selection-toolbar--height: $spacing-unit * 1.5; text-overflow: ellipsis; .icon { - &--disk { height: 14px; margin-right: 8px; @@ -109,11 +101,8 @@ $selection-toolbar--height: $spacing-unit * 1.5; } .directory-tree { - &__checkbox { - .checkbox { - &__decoy { height: 18px; width: 18px; @@ -124,7 +113,6 @@ $selection-toolbar--height: $spacing-unit * 1.5; } &__tree { - .directory-tree__tree { padding-left: 0; } @@ -138,15 +126,10 @@ $selection-toolbar--height: $spacing-unit * 1.5; white-space: nowrap; &:hover > .file__label .directory-tree { - &__checkbox { - .directory-tree { - &__checkbox { - &__item { - &--icon { opacity: 0; } @@ -155,9 +138,7 @@ $selection-toolbar--height: $spacing-unit * 1.5; opacity: 1; .checkbox { - &__decoy { - &:after { opacity: 1; } @@ -171,15 +152,10 @@ $selection-toolbar--height: $spacing-unit * 1.5; } &--selected .directory-tree { - &__checkbox { - .directory-tree { - &__checkbox { - &__item { - &--icon { opacity: 0.75; } @@ -214,7 +190,6 @@ $selection-toolbar--height: $spacing-unit * 1.5; width: auto; .icon { - &--file { transition: fill 0.25s; } @@ -232,7 +207,6 @@ $selection-toolbar--height: $spacing-unit * 1.5; } &--priority { - .icon { height: auto; margin-right: 0; @@ -286,7 +260,6 @@ $selection-toolbar--height: $spacing-unit * 1.5; z-index: 2; .checkbox { - &__decoy { height: 16px; margin-right: 0; @@ -300,7 +273,6 @@ $selection-toolbar--height: $spacing-unit * 1.5; z-index: 1; .icon { - &.icon--folder { margin: -1px 0 0 1px; } @@ -310,11 +282,8 @@ $selection-toolbar--height: $spacing-unit * 1.5; } & > .directory-tree { - &__tree { - & > .directory-tree { - &__node { padding-left: 0; } @@ -323,7 +292,6 @@ $selection-toolbar--height: $spacing-unit * 1.5; } .icon { - &--file, &--folder { height: 14px; @@ -337,11 +305,8 @@ $selection-toolbar--height: $spacing-unit * 1.5; } .directory-tree { - &__node { - .file { - &__label { align-items: center; display: flex; diff --git a/client/src/sass/components/_dropdown.scss b/client/src/sass/components/_dropdown.scss index 183a076e8..d7dac4b76 100644 --- a/client/src/sass/components/_dropdown.scss +++ b/client/src/sass/components/_dropdown.scss @@ -41,7 +41,8 @@ $dropdown--item--foreground--active: $menu--item--foreground--active; &__button { display: block; - padding: $dropdown--button--padding--top $dropdown--button--padding--right + $dropdown--arrow--width + $dropdown--arrow--margin $dropdown--button--padding--bottom $dropdown--button--padding--left; + padding: $dropdown--button--padding--top $dropdown--button--padding--right + $dropdown--arrow--width + + $dropdown--arrow--margin $dropdown--button--padding--bottom $dropdown--button--padding--left; text-align: left; width: auto; @@ -87,6 +88,13 @@ $dropdown--item--foreground--active: $menu--item--foreground--active; color: $dropdown--value; transition: color 0.25s; + @media (max-width: 515px) { + max-width: 15vw; + display: block; + overflow: hidden; + text-overflow: ellipsis; + } + &:after { border-top: 5px solid $dropdown--value; border-left: $dropdown--arrow--width / 2 solid transparent; @@ -136,9 +144,7 @@ $dropdown--item--foreground--active: $menu--item--foreground--active; } &--match-button-width { - .dropdown { - &__content { max-width: 100%; } @@ -153,11 +159,8 @@ $dropdown--item--foreground--active: $menu--item--foreground--active; z-index: 10; .dropdown { - &__header { - .dropdown { - &__value { color: $dropdown--value--active; } diff --git a/client/src/sass/components/_dropzone.scss b/client/src/sass/components/_dropzone.scss index 2e4f4e2f1..bf557ce88 100644 --- a/client/src/sass/components/_dropzone.scss +++ b/client/src/sass/components/_dropzone.scss @@ -22,7 +22,6 @@ $dropzone--file--foreground: $dropzone--foreground; border-color: $input--inverse--border--hover; .dropzone__icon { - .icon { fill: $dropzone--icon--fill--hover; } @@ -30,14 +29,12 @@ $dropzone--file--foreground: $dropzone--foreground; } &--is-dragging { - &, &:hover { border-color: $dropzone--border--dragging; color: $dropzone--foreground--dragging; .dropzone__icon { - .icon { fill: $dropzone--icon--fill--dragging; } @@ -46,9 +43,7 @@ $dropzone--file--foreground: $dropzone--foreground; } &__icon { - .icon { - &--files { fill: $dropzone--icon--fill; height: 64px; @@ -56,7 +51,6 @@ $dropzone--file--foreground: $dropzone--foreground; width: 64px; &__file { - &--right, &--left { fill-opacity: 0.5; @@ -97,9 +91,7 @@ $dropzone--file--foreground: $dropzone--foreground; } &__file { - .icon { - &--file { display: inline-block; height: 14px; @@ -114,9 +106,7 @@ $dropzone--file--foreground: $dropzone--foreground; } &:hover { - .icon { - &--file { opacity: 0.5; } diff --git a/client/src/sass/components/_duration.scss b/client/src/sass/components/_duration.scss index 4cb231bb5..29d21456e 100644 --- a/client/src/sass/components/_duration.scss +++ b/client/src/sass/components/_duration.scss @@ -1,5 +1,4 @@ .duration { - &--segment { margin-right: 0.25em; diff --git a/client/src/sass/components/_filesystem.scss b/client/src/sass/components/_filesystem.scss index 3acc78178..4cb599456 100644 --- a/client/src/sass/components/_filesystem.scss +++ b/client/src/sass/components/_filesystem.scss @@ -4,32 +4,31 @@ $filesystem--directory-list--background--hover: rgba($filesystem--directory-list $filesystem--directory-list--parent--border-color: lighten($filesystem--directory-list--foreground, 43%); .filesystem { - &__directory-list { color: $filesystem--directory-list--foreground; list-style: none; &__item { + opacity: 0.5; padding: $spacing--xx-small $spacing--small; transition: color 0.25s; white-space: nowrap; &--parent, - &--directory { + &--selectable { + opacity: 1; cursor: pointer; transition: background $speed--x-fast, color $speed--x-fast; user-select: none; &:hover { color: $filesystem--directory-list--foreground--hover; + background: $filesystem--directory-list--background--hover; } } - &--directory { - - &:hover { - background: $filesystem--directory-list--background--hover; - } + &--message { + opacity: 1; } &--parent { @@ -46,10 +45,6 @@ $filesystem--directory-list--parent--border-color: lighten($filesystem--director margin-bottom: 0; } } - - &--file { - opacity: 0.5; - } } .icon { diff --git a/client/src/sass/components/_floating-action.scss b/client/src/sass/components/_floating-action.scss index bcb29362a..fdfdf2dda 100644 --- a/client/src/sass/components/_floating-action.scss +++ b/client/src/sass/components/_floating-action.scss @@ -6,7 +6,6 @@ $textbox-repeater--button--foreground: rgba(#12191f, 0.7); $textbox-repeater--button--foreground--hover: #12191f; .floating-action { - &__button { background: $textbox-repeater--button--background; border: none; @@ -46,7 +45,6 @@ $textbox-repeater--button--foreground--hover: #12191f; } &--search { - .icon { height: 15px; width: 15px; @@ -55,7 +53,6 @@ $textbox-repeater--button--foreground--hover: #12191f; } &__group { - &--on-textbox { display: flex; margin-top: -8px; diff --git a/client/src/sass/components/_icons.scss b/client/src/sass/components/_icons.scss index 3ce788c0c..be1c1a69e 100644 --- a/client/src/sass/components/_icons.scss +++ b/client/src/sass/components/_icons.scss @@ -19,11 +19,8 @@ } .icon { - &--eta { - .icon { - &__ring { fill-opacity: 1; } @@ -31,7 +28,6 @@ } &--information { - &__fill { fill-opacity: 0.05; } @@ -42,11 +38,8 @@ } &--limits { - .limits { - &__bars { - &--top { fill-opacity: 0.4; } @@ -63,11 +56,8 @@ } &--loading-indicator { - .loading-indicator { - &--dots { - &__dot { animation: loading-indicator-dots-pulse 0.6s linear alternate infinite; fill: $white; diff --git a/client/src/sass/components/_interactive-list.scss b/client/src/sass/components/_interactive-list.scss index 6f7fbe2c8..d7b7f8eaa 100644 --- a/client/src/sass/components/_interactive-list.scss +++ b/client/src/sass/components/_interactive-list.scss @@ -71,7 +71,7 @@ $interactive-list--detail-list--item--padding--vertical: 0; .interactive-list { &__icon { &--action { - background: $grey; + background: $darkest-grey--hard; &--warning { background: $red; @@ -114,7 +114,7 @@ $interactive-list--detail-list--item--padding--vertical: 0; flex: 0 0 auto; &.tag { - color: lighten($grey, 50%); + color: lighten($darkest-grey--hard, 50%); margin-right: 0; padding-bottom: $spacing--xx-small / 2; padding-top: $spacing--xx-small / 2; diff --git a/client/src/sass/components/_loading-indicator.scss b/client/src/sass/components/_loading-indicator.scss index 539e3e72f..a350df33b 100644 --- a/client/src/sass/components/_loading-indicator.scss +++ b/client/src/sass/components/_loading-indicator.scss @@ -23,9 +23,7 @@ $loading-indicator--tick--background--inverse: rgba($blue, 0.75); width: 32px; &.is-inverse { - .loading-indicator { - &__bar { background: $loading-indicator--bar--background--inverse; diff --git a/client/src/sass/components/_mediainfo.scss b/client/src/sass/components/_mediainfo.scss index cdf621e9c..90af89411 100644 --- a/client/src/sass/components/_mediainfo.scss +++ b/client/src/sass/components/_mediainfo.scss @@ -1,6 +1,7 @@ .mediainfo { display: flex; flex-direction: column; + overflow: auto; &__toolbar { display: flex; @@ -17,9 +18,7 @@ } &__copy-button { - &.tooltip { - &__wrapper { position: absolute; right: 0; @@ -36,6 +35,6 @@ &__output { flex: 1 1 auto; - overflow: auto; + width: max-content; } } diff --git a/client/src/sass/components/_modals.scss b/client/src/sass/components/_modals.scss index ca12f49cd..c247f774f 100644 --- a/client/src/sass/components/_modals.scss +++ b/client/src/sass/components/_modals.scss @@ -152,6 +152,14 @@ $modal--tabs--padding--vertical--left: $modal--padding--horizontal; transform: translate(-50%, 0); transform-origin: 50% 50%; width: 500px; + + @media (max-width: 720px) { + max-width: 100%; + height: 80%; + width: 100%; + top: unset; + bottom: 0%; + } } &--nested-scroll { @@ -356,6 +364,10 @@ $modal--tabs--padding--vertical--left: $modal--padding--horizontal; &__content { &__wrapper { bottom: 10%; + + @media (max-width: 720px) { + bottom: 0%; + } } } } diff --git a/client/src/sass/components/_notifications.scss b/client/src/sass/components/_notifications.scss index 503ce0980..203f8b66f 100644 --- a/client/src/sass/components/_notifications.scss +++ b/client/src/sass/components/_notifications.scss @@ -1,15 +1,12 @@ $notification--sub-heading--color: saturate(lighten(#7189a8, 5%), 5%); .notifications { - &--empty { text-align: center; } &--is-loading { - .notifications { - &__list { opacity: 0.25; } @@ -64,9 +61,7 @@ $notification--sub-heading--color: saturate(lighten(#7189a8, 5%), 5%); width: 24px; .loading-indicator { - &--dots { - &__dot { fill: currentColor; } @@ -76,11 +71,8 @@ $notification--sub-heading--color: saturate(lighten(#7189a8, 5%), 5%); } &__toolbar { - .toolbar { - &__item { - &--button { flex-basis: 33.33%; } @@ -90,7 +82,6 @@ $notification--sub-heading--color: saturate(lighten(#7189a8, 5%), 5%); } .notification { - &__heading { color: #495d76; font-size: 0.75rem; @@ -102,7 +93,6 @@ $notification--sub-heading--color: saturate(lighten(#7189a8, 5%), 5%); } &__message { - &__sub-heading { color: $notification--sub-heading--color; font-style: italic; diff --git a/client/src/sass/components/_peers-list.scss b/client/src/sass/components/_peers-list.scss index 07102fa0d..e2b5e761c 100644 --- a/client/src/sass/components/_peers-list.scss +++ b/client/src/sass/components/_peers-list.scss @@ -1,12 +1,11 @@ .peers-list { - &__flag { display: inline-block; height: 10px; overflow: hidden; - margin-right: $spacing-unit * 3/10; + margin-right: $spacing-unit * 1/5; position: relative; - width: 15px; + width: 20px; vertical-align: baseline; &__image { @@ -32,7 +31,14 @@ } &__encryption { + .icon { + fill: $green; + height: 12px; + width: 12px; + } + } + &__incoming { .icon { fill: $green; height: 12px; diff --git a/client/src/sass/components/_priority-meter.scss b/client/src/sass/components/_priority-meter.scss index 67f2b979a..80d04506c 100644 --- a/client/src/sass/components/_priority-meter.scss +++ b/client/src/sass/components/_priority-meter.scss @@ -4,6 +4,8 @@ $priority-meter--track--level-1--background: rgba($blue, 0.2); $priority-meter--bar--level-1--background: $blue; $priority-meter--track--level-2--background: rgba($green, 0.2); $priority-meter--bar--level-2--background: $green; +$priority-meter--track--level-3--background: rgba($red, 0.2); +$priority-meter--bar--level-3--background: $red; .priority-meter { height: 8px; @@ -43,9 +45,7 @@ $priority-meter--bar--level-2--background: $green; width: 18px; &.priority-meter { - &--level-0 { - &:before { background: $priority-meter--track--level-0--background; } @@ -57,7 +57,6 @@ $priority-meter--bar--level-2--background: $green; } &--level-1 { - &:before { background: $priority-meter--track--level-1--background; } @@ -69,7 +68,6 @@ $priority-meter--bar--level-2--background: $green; } &--level-2 { - &:before { background: $priority-meter--track--level-2--background; } @@ -86,9 +84,7 @@ $priority-meter--bar--level-2--background: $green; width: 26px; &.priority-meter { - &--level-0 { - &:before { background: $priority-meter--track--level-0--background; } @@ -100,7 +96,6 @@ $priority-meter--bar--level-2--background: $green; } &--level-1 { - &:before { background: $priority-meter--track--level-1--background; } @@ -112,7 +107,6 @@ $priority-meter--bar--level-2--background: $green; } &--level-2 { - &:before { background: $priority-meter--track--level-2--background; } @@ -124,13 +118,12 @@ $priority-meter--bar--level-2--background: $green; } &--level-3 { - &:before { - background: $priority-meter--track--level-2--background; + background: $priority-meter--track--level-3--background; } &:after { - background: $priority-meter--bar--level-2--background; + background: $priority-meter--bar--level-3--background; left: 24px; } } diff --git a/client/src/sass/components/_progress-bar.scss b/client/src/sass/components/_progress-bar.scss index 1e1caaa62..cada15c71 100644 --- a/client/src/sass/components/_progress-bar.scss +++ b/client/src/sass/components/_progress-bar.scss @@ -10,26 +10,6 @@ $progress-bar--height: 3px; -$progress-bar--fill: $green; -$progress-bar--fill--checking: #8899a8; -$progress-bar--fill--completed: $blue; -$progress-bar--fill--error: #e95779; -$progress-bar--fill--error--stopped: #f2acbc; -$progress-bar--fill--selected: #fff; -$progress-bar--fill--selected--stopped: #5daaeb; -$progress-bar--fill--stopped: #e7ebee; - -$progress-bar--track--background: rgba($green, 0.15); -$progress-bar--track--background--checking: rgba(#8899a8, 0.15); -$progress-bar--track--background--completed: rgba($blue, 0.15); -$progress-bar--track--background--error: rgba(#e95779, 0.15); -$progress-bar--track--background--error--stopped: rgba(#f2acbc, 0.15); -$progress-bar--track--background--selected: rgba(#fff, 0.15); -$progress-bar--track--background--selected--stopped: rgba(#5daaeb, 0.15); -$progress-bar--track--background--selected--stopped: rgba(#fff, 0.15); -$progress-bar--track--background--stopped: rgba(#e7ebee, 0.35); -$progress-bar--track--background--selected: rgba(#fff, 0.15); - .progress-bar { align-items: center; display: flex; @@ -49,115 +29,117 @@ $progress-bar--track--background--selected: rgba(#fff, 0.15); width: 12px; .torrent--is-seeding & { - color: $progress-bar--fill--completed; + @include theme('color', 'progress-bar--fill--completed'); } .torrent--is-stopped & { - color: $progress-bar--fill--stopped; + @include theme('color', 'progress-bar--fill--stopped'); } .torrent--has-error & { - color: $progress-bar--fill--error; + @include theme('color', 'progress-bar--fill--error'); } .torrent--is-checking & { - color: $progress-bar--fill--checking; + @include theme('color', 'progress-bar--fill--checking'); } .torrent--is-selected & { - color: $progress-bar--fill--selected; + @include theme('color', 'progress-bar--fill--selected'); } .torrent--is-selected.torrent--is-stopped & { - color: $progress-bar--fill--selected--stopped; + @include theme('color', 'progress-bar--fill--selected--stopped'); } .torrent--has-error.torrent--is-stopped & { - color: $progress-bar--fill--error--stopped; + @include theme('color', 'progress-bar--fill--error--stopped'); } } } &__fill { - background: $progress-bar--fill; + @include theme('background', 'progress-bar--fill'); display: block; height: $progress-bar--height; width: 100%; .torrent--is-seeding & { - background: $progress-bar--fill--completed; + @include theme('background', 'progress-bar--fill--completed'); } .torrent--is-stopped & { - background: $progress-bar--fill--stopped; + @include theme('background', 'progress-bar--fill--stopped'); } .torrent--has-error & { - background: $progress-bar--fill--error; + @include theme('background', 'progress-bar--fill--error'); } .torrent--is-checking & { - background: $progress-bar--fill--checking; + @include theme('background', 'progress-bar--fill--checking'); } .torrent--is-selected & { - background: $progress-bar--fill--selected; + @include theme('background', 'progress-bar--fill--selected'); } .torrent--is-selected.torrent--is-stopped & { - background: $progress-bar--fill--selected--stopped; + @include theme('background', 'progress-bar--fill--selected--stopped'); } .torrent--has-error.torrent--is-stopped & { - background: $progress-bar--fill--error--stopped; + @include theme('background', 'progress-bar--fill--error--stopped'); } &__wrapper { - background: $progress-bar--track--background; + @include theme('background', 'progress-bar--track--background'); flex: 1 1 auto; position: relative; height: $progress-bar--height; .torrent--is-stopped & { - background: $progress-bar--track--background--stopped; + @include theme('background', 'progress-bar--track--background--stopped'); } .torrent--has-error & { - background: $progress-bar--track--background--error; + @include theme('background', 'progress-bar--track--background--error'); } .torrent--is-selected & { - background: $progress-bar--track--background--selected; + @include theme('background', 'progress-bar--track--background--selected'); } .torrent--has-error.torrent--is-stopped & { - background: $progress-bar--track--background--error--stopped; + @include theme('background', 'progress-bar--track--background--error--stopped'); } .torrent--is-selected.torrent--is-stopped & { - background: $progress-bar--track--background--selected--stopped; + @include theme('background', 'progress-bar--track--background--selected--stopped'); opacity: 1; } .torrent--is-checking & { animation: candy-stripe 0.25s linear infinite; background-color: transparent; - background-image: linear-gradient(-45deg, rgba($progress-bar--track--background--checking, 0) 0, - rgba($progress-bar--track--background--checking, 0) 25%, rgba($progress-bar--track--background--checking, 0.5) 25%, - rgba($progress-bar--track--background--checking, 0.5) 50%, rgba($progress-bar--track--background--checking, 0) 50%, - rgba($progress-bar--track--background--checking, 0) 75%, rgba($progress-bar--track--background--checking, 0.5) 75%, - rgba($progress-bar--track--background--checking, 0.5) 100%); + @include theme('background-image', 'progress-bar--track--background--checking'); background-size: 4px 4px; height: $progress-bar--height; top: 0; } .torrent--is-selected.torrent--is-checking & { - background-image: linear-gradient(-45deg, rgba(#fff, 0) 0, - rgba(#fff, 0) 25%, rgba(#fff, 0.5) 25%, - rgba(#fff, 0.5) 50%, rgba(#fff, 0) 50%, - rgba(#fff, 0) 75%, rgba(#fff, 0.5) 75%, - rgba(#fff, 0.5) 100%); + background-image: linear-gradient( + -45deg, + rgba(#fff, 0) 0, + rgba(#fff, 0) 25%, + rgba(#fff, 0.5) 25%, + rgba(#fff, 0.5) 50%, + rgba(#fff, 0) 50%, + rgba(#fff, 0) 75%, + rgba(#fff, 0.5) 75%, + rgba(#fff, 0.5) 100% + ); } } } diff --git a/client/src/sass/components/_scrollbars.scss b/client/src/sass/components/_scrollbars.scss index 4fc215e1f..db44a51f4 100644 --- a/client/src/sass/components/_scrollbars.scss +++ b/client/src/sass/components/_scrollbars.scss @@ -1,47 +1,127 @@ -$scrollbar--thumb--background--inactive: rgba(#1a2f3d, 0.3); -$scrollbar--thumb--background--hover: rgba(#1a2f3d, 0.6); -$scrollbar--thumb--background--inverted--inactive: rgba(#e9eef2, 0.3); -$scrollbar--thumb--background--inverted--hover: rgba(#e9eef2, 0.6); - -.scrollbars { - - &__thumb { - background: $scrollbar--thumb--background--inactive; - border-radius: 10px; - cursor: pointer; - opacity: 0; - transition: background 0.25s, opacity 0.5s; - z-index: 2; - - &:active { - opacity: 1; - } - - &:hover, - &:active { - background: $scrollbar--thumb--background--hover; - } - - &--surrogate { - display: block; - height: 100%; - width: 100%; - } - - .is-inverted & { - background: $scrollbar--thumb--background--inverted--inactive; - - &:hover, - &:active { - background: $scrollbar--thumb--background--inverted--hover; - } - } - } - - &:hover { - - .scrollbars__thumb { - opacity: 1; - } - } +* { + scrollbar-width: thin; +} + +::-webkit-scrollbar { + height: 6px; + width: 6px; +} + +::-webkit-scrollbar-corner { + background: none; +} + +::-webkit-scrollbar-track { + opacity: 0; +} + +::-webkit-scrollbar-thumb { + border-radius: 4px; + background: #8d8d8d; +} + +::-webkit-scrollbar-thumb:hover { + border-radius: 4px; + background: lighten(#8d8d8d, 10%); +} + +.os-theme-thin > .os-scrollbar-horizontal { + right: 14px; + height: 14px; + padding: 0px 6px; +} + +.os-theme-thin > .os-scrollbar-vertical { + bottom: 14px; + width: 14px; + padding: 6px 0px; +} + +.os-theme-thin.os-host-rtl > .os-scrollbar-horizontal { + left: 14px; + right: 0; +} + +.os-theme-thin > .os-scrollbar-corner { + height: 14px; + width: 14px; + background-color: transparent; +} + +.os-theme-thin > .os-scrollbar > .os-scrollbar-track { + background: transparent; +} + +.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track:before, +.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track:before { + content: ''; + display: block; + position: absolute; + background: rgba(255, 255, 255, 0.15); +} + +.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track:before, +.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle:before { + left: 0; + right: 0; + height: 2px; + top: 50%; + margin-top: -1px; +} + +.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track:before, +.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle:before { + top: 0; + bottom: 0; + width: 2px; + left: 50%; + margin-left: -1px; +} + +.os-theme-thin > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle:before { + content: ''; + display: block; + position: absolute; + background: rgba(255, 255, 255, 0.5); + border-radius: 10px; +} + +.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle:hover:before, +.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle.active:before { + height: 4px; + margin-top: -2px; +} + +.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle:hover:before, +.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle.active:before { + width: 4px; + margin-left: -2px; +} + +.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle:hover:before, +.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle:hover:before { + background: rgba(255, 255, 255, 0.7); +} + +.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle.active:before, +.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle.active:before { + background: #fff; +} + +.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle { + height: 100%; + min-width: 30px; +} + +.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle { + width: 100%; + min-height: 30px; +} + +.os-theme-thin.os-host-transition > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle:before { + transition: height 0.3s, margin-top 0.3s, background 0.2s; +} + +.os-theme-thin.os-host-transition > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle:before { + transition: width 0.3s, margin-left 0.3s, background 0.2s; } diff --git a/client/src/sass/components/_search.scss b/client/src/sass/components/_search.scss index aef43704e..3cf288bea 100644 --- a/client/src/sass/components/_search.scss +++ b/client/src/sass/components/_search.scss @@ -78,7 +78,6 @@ $search-torrents--icon--foreground--active: $blue; } &.is-in-use { - .icon { fill: $search-torrents--icon--foreground--active; opacity: 1; diff --git a/client/src/sass/components/_sidebar-filter.scss b/client/src/sass/components/_sidebar-filter.scss index a37756aca..ea02e252c 100644 --- a/client/src/sass/components/_sidebar-filter.scss +++ b/client/src/sass/components/_sidebar-filter.scss @@ -7,36 +7,37 @@ } &__item { - color: $sidebar-filter--foreground; + @include theme('color', 'sidebar-filter--foreground'); cursor: pointer; font-weight: 400; padding: 3px 20px; transition: color 0.25s; &:hover { - color: $sidebar-filter--foreground--hover; + @include theme('color', 'sidebar-filter--foreground--hover'); .icon { - fill: $sidebar-filter--foreground--hover; + @include theme('fill', 'sidebar-filter--foreground--hover'); } } &.is-active { - color: $sidebar-filter--foreground--active; + @include theme('color', 'sidebar-filter--foreground--active'); font-weight: 700; .badge { - background: $sidebar-filter--count--background--active; + @include theme('background', 'sidebar-filter--count--background--active'); + @include theme('color', 'sidebar-filter--count--foreground--active'); } .icon { - fill: $sidebar-filter--foreground--active; + @include theme('fill', 'sidebar-filter--foreground--active'); } } .icon { display: inline-block; - fill: $sidebar-filter--foreground; + @include theme('fill', 'sidebar-filter--foreground'); height: 14px; margin-right: 7px; transition: fill 0.25s; @@ -46,16 +47,13 @@ } .badge { - background: $sidebar-filter--count--background; - color: $sidebar-filter--count--foreground; + @include theme('background', 'sidebar-filter--count--background'); + @include theme('color', 'sidebar-filter--count--foreground'); } } - .sidebar-filter { - &__item { - &--heading { cursor: default; font-size: 0.8em; @@ -65,7 +63,7 @@ &, &:hover { - color: $sidebar-filter--foreground--header; + @include theme('color', 'sidebar-filter--foreground--header'); } } } diff --git a/client/src/sass/components/_sidebar.scss b/client/src/sass/components/_sidebar.scss index 48762f756..e32d9d4f3 100644 --- a/client/src/sass/components/_sidebar.scss +++ b/client/src/sass/components/_sidebar.scss @@ -1,33 +1,32 @@ -$sidebar--foreground: #526780; -$sidebar--border: rgba(darken($sidebar--foreground, 40%), 0.3); - -$sidebar-filter--foreground: $sidebar--foreground; -$sidebar-filter--foreground--header: rgba($sidebar-filter--foreground, 0.5); -$sidebar-filter--foreground--active: $blue; -$sidebar-filter--foreground--hover: lighten($sidebar-filter--foreground, 15%); - -$sidebar--icon-button--fill: $sidebar--foreground; -$sidebar--icon-button--fill--hover: $blue; -$sidebar--icon-button--foreground: rgba($sidebar--foreground, 0.7); -$sidebar--icon-button--foreground--hover: $blue; - .application { &__sidebar { - background: $sidebar--background; - box-shadow: 1px 0 $sidebar--border; - color: $sidebar--foreground; + @include theme('background', 'sidebar--background'); + @include theme('box-shadow', 'sidebar--border'); + @include theme('color', 'sidebar--foreground'); flex: 1; - min-width: 200px; - max-width: 240px; - overflow: auto; + min-width: 240px; + max-width: 250px; + height: 100%; + overflow-x: hidden; + overflow-y: overlay; position: relative; z-index: 2; + transition: transform 0.2s; + + .os-content { + display: flex; + flex-direction: column; + } + + @media (max-width: 720px) { + transform: translateX(-120px); + } } } .sidebar { &__icon-button { - color: $sidebar--icon-button--foreground; + @include theme('color', 'sidebar--icon-button--foreground'); display: block; font-size: 0.8em; line-height: 1; @@ -36,10 +35,10 @@ $sidebar--icon-button--foreground--hover: $blue; transition: color 0.25s; &:hover { - color: $sidebar--icon-button--foreground--hover; + @include theme('color', 'sidebar--icon-button--foreground--hover'); .icon { - fill: $sidebar--icon-button--fill--hover; + @include theme('fill', 'sidebar--icon-button--fill--hover'); } } @@ -48,7 +47,7 @@ $sidebar--icon-button--foreground--hover: $blue; } .icon { - fill: $sidebar--icon-button--fill; + @include theme('fill', 'sidebar--icon-button--fill'); height: 13px; transition: fill 0.25s; position: relative; @@ -65,6 +64,7 @@ $sidebar--icon-button--foreground--hover: $blue; } &__actions { + min-height: max-content; display: flex; padding: $spacing-unit * 1/5; justify-content: flex-start; @@ -75,10 +75,10 @@ $sidebar--icon-button--foreground--hover: $blue; display: none; } .progress-bar__fill__wrapper { - background: rgba($sidebar-filter--foreground, 0.5); + @include theme('background', 'sidebar-filter--foreground--fill'); } .progress-bar__fill { - background: $sidebar-filter--foreground; + @include theme('background', 'sidebar-filter--foreground'); } } } @@ -103,7 +103,7 @@ $sidebar--icon-button--foreground--hover: $blue; } &__label { - color: $light-grey; + color: $dark-grey; display: block; font-size: 0.9em; font-weight: 600; diff --git a/client/src/sass/components/_sort-dropdown.scss b/client/src/sass/components/_sort-dropdown.scss index a5df2d94b..a8eca9ccf 100644 --- a/client/src/sass/components/_sort-dropdown.scss +++ b/client/src/sass/components/_sort-dropdown.scss @@ -1,5 +1,4 @@ .sort-dropdown { - &__item { align-items: center; display: flex; diff --git a/client/src/sass/components/_sortable-list.scss b/client/src/sass/components/_sortable-list.scss index bcc5a388c..e8a71c8d9 100644 --- a/client/src/sass/components/_sortable-list.scss +++ b/client/src/sass/components/_sortable-list.scss @@ -59,17 +59,15 @@ $sortable-list--item--border--preview: darken($sortable-list--item--border, 3%); } & + .sortable-list { - &__item { margin-top: -1px; } } .toggle-input { - &__indicator { background: darken($input--inverse--background, 5%); - border-color: darken($input--inverse--background, 8%); + border-color: darken($input--inverse--background, 8%); } } @@ -120,4 +118,10 @@ $sortable-list--item--border--preview: darken($sortable-list--item--border, 3%); } } } + + &--torrent-context-menu-items { + .sortable-list__item { + cursor: default; + } + } } diff --git a/client/src/sass/components/_table.scss b/client/src/sass/components/_table.scss index 22afa356a..80f627d1e 100644 --- a/client/src/sass/components/_table.scss +++ b/client/src/sass/components/_table.scss @@ -1,26 +1,16 @@ -$table--heading--background: rgba(#f6f8fA, 0.6); -$table--heading--border: rgba(#1d2938, 0.1); -$table--heading--border--horizontal: rgba(#1d2938, 0.08); - -$table--heading--color: #abbac7; -$table--heading--color--hover: darken($table--heading--color, 10%); -$table--heading--color--active: darken($table--heading--color, 30%); -$table--heading--color--active--hover: darken($table--heading--color, 40%); - $table--heading--resize--handle--width: 9px; $table--heading--resize--indicator--width: 1px; -$table--heading--resize--indicator--background: rgba(darken(#f6f8fA, 10%), 0.75); .table { - &__row { - &--heading { - background: $table--heading--background; - box-shadow: 0 1px 0 $table--heading--border; - color: $table--heading--color; + @include theme('background', 'table--heading--background'); + @include theme('box-shadow', 'table--heading--border'); + @include theme('color', 'table--heading--color'); display: flex; height: 24px; + min-height: 24px; + overflow: hidden; font-size: 12px; white-space: nowrap; z-index: 1; @@ -29,19 +19,20 @@ $table--heading--resize--indicator--background: rgba(darken(#f6f8fA, 10%), 0.75) &__heading { align-items: center; - border-right: 1px solid $table--heading--border--horizontal; + @include theme('border-right', 'table--heading--border--horizontal'); cursor: pointer; display: flex; flex: 0 0 auto; position: relative; transition: color 0.15s; + touch-action: none; &:last-child { border-right: none; } &:hover { - color: $table--heading--color--hover; + @include theme('color', 'table--heading--color--hover'); } &:after { @@ -58,11 +49,11 @@ $table--heading--resize--indicator--background: rgba(darken(#f6f8fA, 10%), 0.75) } &--is-sorted { - color: $table--heading--color--active; + @include theme('color', 'table--heading--color--active'); font-weight: 700; &:hover { - color: $table--heading--color--active--hover; + @include theme('color', 'table--heading--color--active--hover'); } &:after { @@ -71,9 +62,7 @@ $table--heading--resize--indicator--background: rgba(darken(#f6f8fA, 10%), 0.75) } &--direction { - &--asc { - &:after { transform: rotate(180deg); } @@ -85,7 +74,6 @@ $table--heading--resize--indicator--background: rgba(darken(#f6f8fA, 10%), 0.75) min-width: 0; &.table { - &__heading { border: none; margin: 0; @@ -106,7 +94,7 @@ $table--heading--resize--indicator--background: rgba(darken(#f6f8fA, 10%), 0.75) z-index: 2; &:after { - background: $table--heading--resize--indicator--background; + @include theme('background', 'table--heading--resize--indicator--background'); bottom: 0; content: ''; left: ($table--heading--resize--handle--width - $table--heading--resize--indicator--width) / 2; @@ -131,11 +119,11 @@ $table--heading--resize--indicator--background: rgba(darken(#f6f8fA, 10%), 0.75) } &__resize-line { - background: $table--heading--resize--indicator--background; + @include theme('background', 'table--heading--resize--indicator--background'); bottom: 0; left: 0; opacity: 0; - position: absolute; + position: fixed; top: 0; transition: opacity 0.125s; will-change: opacity, transform; @@ -143,7 +131,7 @@ $table--heading--resize--indicator--background: rgba(darken(#f6f8fA, 10%), 0.75) } &__column-fill { - background: $table--heading--resize--indicator--background; + @include theme('background', 'table--heading--resize--indicator--background'); bottom: 0; left: 0; opacity: 0; diff --git a/client/src/sass/components/_textbox-repeater.scss b/client/src/sass/components/_textbox-repeater.scss index 50894717c..4fef69c2b 100644 --- a/client/src/sass/components/_textbox-repeater.scss +++ b/client/src/sass/components/_textbox-repeater.scss @@ -1,5 +1,4 @@ .textbox-repeater { - .icon { height: 12px; width: 12px; diff --git a/client/src/sass/components/_toolbar.scss b/client/src/sass/components/_toolbar.scss index 1e4c42557..f78501bdf 100644 --- a/client/src/sass/components/_toolbar.scss +++ b/client/src/sass/components/_toolbar.scss @@ -5,9 +5,7 @@ width: 100%; &--dark { - &.toolbar { - &--bottom { border-top: 1px solid $dark-blue; } @@ -18,9 +16,7 @@ } .toolbar { - &__item { - &--button { color: darken($foreground, 10%); diff --git a/client/src/sass/components/_tooltip.scss b/client/src/sass/components/_tooltip.scss index ef3cef1bc..934b2fcf7 100644 --- a/client/src/sass/components/_tooltip.scss +++ b/client/src/sass/components/_tooltip.scss @@ -38,6 +38,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; line-height: $tooltip-line-height; padding: $tooltip-padding-vertical $tooltip-padding-horizontal; position: relative; + user-select: none; &--no-padding { padding: 0; @@ -70,21 +71,15 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; } &--position { - &--bottom, &--top { - &.tooltip { - &--anchor { - &--center { transform: translateX(-50%); .tooltip { - &__content { - &:after { left: 50%; transform: translateX(-50%); @@ -94,11 +89,8 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; } &--start { - &.tooltip { - &--align { - &--center { transform: translateX($tooltip-anchor-offset * -1); } @@ -106,9 +98,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; } .tooltip { - &__content { - &:after { left: $tooltip-arrow-offset; } @@ -120,9 +110,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; transform: translateX(calc(-100% + #{$tooltip-anchor-offset})); .tooltip { - &__content { - &:after { right: $tooltip-arrow-offset; } @@ -137,9 +125,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; padding-top: $tooltip-arrow-border-width; .tooltip { - &__content { - &:after { border-left-color: transparent; border-right-color: transparent; @@ -154,9 +140,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; padding-bottom: $tooltip-arrow-border-width; .tooltip { - &__content { - &:after { border-bottom: none; border-left-color: transparent; @@ -169,19 +153,14 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; &--left, &--right { - &.tooltip { - &--anchor { - &--center { top: 50%; transform: translateY(-50%); .tooltip { - &__content { - &:after { top: 50%; transform: translateY(-50%); @@ -194,9 +173,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; transform: translateY($tooltip-anchor-offset * -1); .tooltip { - &__content { - &:after { top: $tooltip-arrow-offset; } @@ -208,9 +185,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; transform: translateY(calc(-100% + #{$tooltip-anchor-offset})); .tooltip { - &__content { - &:after { bottom: $tooltip-arrow-offset; } @@ -225,9 +200,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; padding-right: $tooltip-arrow-border-width; .tooltip { - &__content { - &:after { border-bottom-color: transparent; border-right: none; @@ -242,9 +215,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; padding-left: $tooltip-arrow-border-width; .tooltip { - &__content { - &:after { border-bottom-color: transparent; border-left: none; @@ -257,9 +228,7 @@ $tooltip-anchor-offset: $tooltip-arrow-offset + $tooltip-arrow-border-width; } &--is-error { - .tooltip { - &__content { background: $tooltip-background--error; color: $tooltip-foreground--error; diff --git a/client/src/sass/components/_torrent-details-panel.scss b/client/src/sass/components/_torrent-details-panel.scss index e765e8cf7..7fcb27ddf 100644 --- a/client/src/sass/components/_torrent-details-panel.scss +++ b/client/src/sass/components/_torrent-details-panel.scss @@ -41,7 +41,6 @@ $torrent-details--tags--background: #515f6f; $torrent-details--tags--foreground: #1a2028; .torrent-details { - &__heading { color: inherit; font-size: inherit; @@ -103,14 +102,10 @@ $torrent-details--tags--foreground: #1a2028; } &__header { - &.is-completed, &.is-stopped { - .progress-bar { - &__icon { - .icon { fill: $torrent-details--header--progress-bar--fill; } @@ -120,7 +115,6 @@ $torrent-details--tags--foreground: #1a2028; background: $torrent-details--header--progress-bar--fill; &__wrapper { - &:after { background: $torrent-details--header--progress-bar--fill; } @@ -152,7 +146,6 @@ $torrent-details--tags--foreground: #1a2028; width: 100%; &__heading { - &--primary { color: $torrent-details--directory-tree--parent-directory--foreground; font-size: 1.125em; @@ -186,14 +179,12 @@ $torrent-details--tags--foreground: #1a2028; margin-left: -8px; .directory-tree { - &__node { color: $directory-tree--foreground; position: relative; transition: background 0.25s, border 0.25s, color 0.25s; &--group { - &:after { background: $directory-tree--group--extension; } @@ -207,7 +198,6 @@ $torrent-details--tags--foreground: #1a2028; font-weight: 500; .icon { - &--folder { fill: $directory-tree--icon--folder--open; } @@ -215,7 +205,6 @@ $torrent-details--tags--foreground: #1a2028; } .icon { - &--folder { fill: $directory-tree--icon--folder; } @@ -247,9 +236,7 @@ $torrent-details--tags--foreground: #1a2028; } .file { - &__detail { - &--size, &--priority { color: $directory-tree--file-details--hover--foreground; @@ -258,7 +245,6 @@ $torrent-details--tags--foreground: #1a2028; } .icon { - &--file { fill: $torrent-details--directory-tree--file--hover--foreground; } @@ -270,7 +256,6 @@ $torrent-details--tags--foreground: #1a2028; width: auto; &__detail { - &--size, &--priority { color: currentColor; @@ -287,7 +272,6 @@ $torrent-details--tags--foreground: #1a2028; color: $torrent-details--directory-tree--parent-directory--foreground; .icon { - &--disk { fill: $torrent-details--directory-tree--parent-directory--icon--fill; } @@ -296,7 +280,6 @@ $torrent-details--tags--foreground: #1a2028; } .icon { - &--file { fill: $directory-tree--icon--file; } @@ -310,16 +293,13 @@ $torrent-details--tags--foreground: #1a2028; } &__detail { - &--hash { - .torrent-details__detail__value { word-break: break-all; } } &--tags { - .tag { background: $torrent-details--tags--background; color: $torrent-details--tags--foreground; @@ -327,7 +307,6 @@ $torrent-details--tags--foreground: #1a2028; } & + .torrent-details__table__heading { - .torrent-details__table__heading--tertiary { padding-top: 10px; } diff --git a/client/src/sass/components/_torrent.scss b/client/src/sass/components/_torrent.scss index 4d1a83c6f..7476727ae 100644 --- a/client/src/sass/components/_torrent.scss +++ b/client/src/sass/components/_torrent.scss @@ -1,44 +1,3 @@ -$torrent--primary--foreground: saturate(darken(#8899a8, 20%), 10%); -$torrent--primary--foreground--error: #e95779; -$torrent--primary--foreground--error--stopped: rgba($torrent--primary--foreground--error, 0.6); -$torrent--primary--foreground--error--stopped--selected: rgba($white, 0.6); -$torrent--primary--foreground--stopped: rgba(#8899a8, 0.8); -$torrent--primary--foreground--selected: $white; -$torrent--primary--foreground--selected--stopped: rgba($torrent--primary--foreground--selected, 0.6); -$torrent--primary--foreground--selected--error: $white; - -$torrent--secondary--foreground: #8899a8; -$torrent--secondary--foreground--stopped: #c3ccd3; -$torrent--secondary--foreground--selected: $white; -$torrent--secondary--foreground--selected--stopped: rgba($white, 0.6); - -$torrent--tertiary--foreground: #8899a8; -$torrent--tertiary--foreground--stopped: #c3ccd3; -$torrent--tertiary--foreground--selected: rgba($white, 0.9); -$torrent--tertiary--foreground--selected--stopped: rgba($white, 0.5); - -$torrent--background--hover: #f6f8fa; -$torrent--background--selected: $blue; -$torrent--background--error: #e95779; - -$torrent--border: #e9eef2; -$torrent--border--selected: darken($torrent--background--selected, 3%); -$torrent--border--selected--error: darken($torrent--background--error, 3%); - -$torrent--speed--foreground--selected--has-error: $white; -$torrent--speed--foreground--selected: $white; - -$torrent--tags--background: #dce2e6; -$torrent--tags--foreground: #6f7d87; -$torrent--tags--background--selected: #4ea3ea; -$torrent--tags--background--selected--has-error: #ed7994; -$torrent--tags--foreground--selected: $white; -$torrent--tags--foreground--has-error: #fbe8ec; - -$more-info--background: $white; -$more-info--background--hover: $white; - -$more-info--box-shadow--hue: #1a2f3d; $more-info--border: $textbox-repeater--button--border; .torrent { @@ -58,13 +17,11 @@ $more-info--border: $textbox-repeater--button--border; } &:hover { - &:after { - background: $torrent--background--hover; + @include theme('background', 'torrent--background--hover'); } .torrent { - &__more-info { opacity: 1; pointer-events: auto; @@ -74,17 +31,15 @@ $more-info--border: $textbox-repeater--button--border; } &__detail { - color: $torrent--secondary--foreground; + @include theme('color', 'torrent--secondary--foreground'); &--name { - color: $torrent--primary--foreground; - font-weight: 500; + @include theme('color', 'torrent--primary--foreground'); + @include theme('font-weight', 'torrent--primary--font-weight'); } &--tags { - .torrent { - &__tag { display: inline-block; margin-right: $spacing-unit * 3/10; @@ -96,15 +51,13 @@ $more-info--border: $textbox-repeater--button--border; } .tag { - background: $torrent--tags--background; - color: $torrent--tags--foreground; + @include theme('background', 'torrent--tags--background'); + @include theme('color', 'torrent--tags--foreground'); } } &__icon { - &--checkmark { - &.icon { opacity: 1; } @@ -120,42 +73,36 @@ $more-info--border: $textbox-repeater--button--border; } &__details { - &__section { - &--secondary { - color: $torrent--secondary--foreground; + @include theme('color', 'torrent--secondary--foreground'); } &--tertiary { - color: $torrent--tertiary--foreground; + @include theme('color', 'torrent--tertiary--foreground'); } } } &--is-stopped { - .torrent { - &__details { - &__section { - &--secondary { - color: $torrent--secondary--foreground--stopped; + @include theme('color', 'torrent--secondary--foreground--stopped'); } &--tertiary { - color: $torrent--tertiary--foreground--stopped; + @include theme('color', 'torrent--tertiary--foreground--stopped'); } } } &__detail { - color: $torrent--secondary--foreground--stopped; + @include theme('color', 'torrent--secondary--foreground--stopped'); &--name { - color: $torrent--primary--foreground--stopped; + @include theme('color', 'torrent--primary--foreground--stopped'); font-weight: 400; } @@ -167,17 +114,12 @@ $more-info--border: $textbox-repeater--button--border; } &--has-error { - &.torrent { - &--is-stopped { - .torrent { - &__detail { - &--name { - color: $torrent--primary--foreground--error--stopped; + @include theme('color', 'torrent--primary--foreground--error--stopped'); } } } @@ -185,28 +127,22 @@ $more-info--border: $textbox-repeater--button--border; } .torrent { - &__detail { - &--name { - color: $torrent--primary--foreground--error; + @include theme('color', 'torrent--primary--foreground--error'); } } } &:after { - background: $torrent--background--error; + @include theme('background', 'torrent--background--error'); } } &--is-downloading { - &--actively { - .torrent { - &__detail { - &--downRate { color: $green; font-weight: 500; @@ -215,16 +151,13 @@ $more-info--border: $textbox-repeater--button--border; } .torrent-details { - &__sub-heading { - &__tertiary { - &--download { - color: $blue; + color: $green; .icon { - fill: $blue; + fill: $green; } } } @@ -234,13 +167,9 @@ $more-info--border: $textbox-repeater--button--border; } &--is-uploading { - &--actively { - .torrent { - &__detail { - &--upRate { color: $blue; font-weight: 500; @@ -249,16 +178,13 @@ $more-info--border: $textbox-repeater--button--border; } .torrent-details { - &__sub-heading { - &__tertiary { - &--upload { - color: $green; + color: $blue; .icon { - fill: $green; + fill: $blue; } } } @@ -268,55 +194,43 @@ $more-info--border: $textbox-repeater--button--border; } &--is-selected { - &.torrent { - &--has-error { - .torrent { - &__detail { - &--name { - color: $torrent--primary--foreground--selected--error; + @include theme('color', 'torrent--primary--foreground--selected--error'); } &--tags { - .tag { - background: $torrent--tags--background--selected--has-error; - color: $torrent--tags--foreground--has-error; + @include theme('background', 'torrent--tags--background--selected--has-error'); + @include theme('color', 'torrent--tags--foreground--has-error'); } } &--downRate, &--upRate { - color: $torrent--speed--foreground--selected--has-error; + @include theme('color', 'torrent--speed--foreground--selected--has-error'); } } } &, &:hover { - &:after { - background: $torrent--background--error; + @include theme('background', 'torrent--background--error'); } } } &--is-stopped { - &.torrent { - &--has-error { - .torrent { - &__detail { - &--name { - color: $torrent--primary--foreground--error--stopped--selected; + @include theme('color', 'torrent--primary--foreground--error--stopped--selected'); } } } @@ -324,17 +238,16 @@ $more-info--border: $textbox-repeater--button--border; } .torrent { - &__detail { - color: $torrent--secondary--foreground--selected--stopped; + @include theme('color', 'torrent--secondary--foreground--selected--stopped'); &--name { - color: $torrent--primary--foreground--selected--stopped; + @include theme('color', 'torrent--primary--foreground--selected--stopped'); } &--downRate, &--upRate { - color: $torrent--tertiary--foreground--selected--stopped; + @include theme('color', 'torrent--tertiary--foreground--selected--stopped'); } &--tags { @@ -343,15 +256,13 @@ $more-info--border: $textbox-repeater--button--border; } &__details { - &__section { - &--secondary { - color: $torrent--secondary--foreground--selected--stopped; + @include theme('color', 'torrent--secondary--foreground--selected--stopped'); } &--tertiary { - color: $torrent--tertiary--foreground--selected--stopped; + @include theme('color', 'torrent--tertiary--foreground--selected--stopped'); } } } @@ -361,46 +272,41 @@ $more-info--border: $textbox-repeater--button--border; &, &:hover { - &:after { - background: $torrent--background--selected; + @include theme('background', 'torrent--background--selected'); opacity: 1; } } .torrent { - &__detail { - color: $torrent--secondary--foreground--selected; + @include theme('color', 'torrent--secondary--foreground--selected'); &--name { - color: $torrent--primary--foreground--selected; + @include theme('color', 'torrent--primary--foreground--selected'); } &--downRate, &--upRate { - color: $torrent--speed--foreground--selected; + @include theme('color', 'torrent--speed--foreground--selected'); } &--tags { - .tag { - background: $torrent--tags--background--selected; - color: $torrent--tags--foreground--selected; + @include theme('background', 'torrent--tags--background--selected'); + @include theme('color', 'torrent--tags--foreground--selected'); } } } &__details { - &__section { - &--secondary { - color: $torrent--secondary--foreground--selected; + @include theme('color', 'torrent--secondary--foreground--selected'); } &--tertiary { - color: $torrent--tertiary--foreground--selected; + @include theme('color', 'torrent--tertiary--foreground--selected'); } } } @@ -416,15 +322,10 @@ $more-info--border: $textbox-repeater--button--border; height: 70px; &.torrent { - &--is-downloading { - &--actively { - .torrent { - &__detail { - &--eta { margin-right: 10px; opacity: 1; @@ -437,9 +338,7 @@ $more-info--border: $textbox-repeater--button--border; } .torrent { - &__details { - &__section { display: flex; @@ -467,7 +366,6 @@ $more-info--border: $textbox-repeater--button--border; } .torrent { - &__detail { width: auto; } @@ -525,6 +423,7 @@ $more-info--border: $textbox-repeater--button--border; } &--isPrivate { + text-overflow: clip; width: 25px; } @@ -541,20 +440,17 @@ $more-info--border: $textbox-repeater--button--border; } &--tags { - &:last-child { margin-left: auto; width: auto; } .torrent { - &__tags { margin-right: 0; } &__tag { - &:last-child { margin-right: 0; } @@ -577,66 +473,58 @@ $more-info--border: $textbox-repeater--button--border; &--is-condensed { align-items: center; - border-top: 1px solid $torrent--border; + @include theme('border-top', 'torrent--border'); display: flex; height: 30px; + min-width: max-content; + max-width: 100%; padding: 0; - &:nth-child(0n + 2) { + &:nth-child(0n + 1) { border-top: none; } &.torrent { - &--is-stopped { - .torrent { - &__detail { - color: $torrent--secondary--foreground--stopped; + @include theme('color', 'torrent--secondary--foreground--stopped'); } } } &--is-selected { - &, & + .torrent { - border-color: $torrent--border--selected; + @include theme('border-color', 'torrent--border--selected'); } &.torrent { - &--has-error { - &, & + .torrent { - border-color: $torrent--border--selected--error; + @include theme('border-color', 'torrent--border--selected--error'); } } &--is-stopped { - .torrent { - &__detail { - color: $torrent--tertiary--foreground--selected--stopped; + @include theme('color', 'torrent--tertiary--foreground--selected--stopped'); } } } } .torrent { - &__detail { - color: $torrent--secondary--foreground--selected; + @include theme('color', 'torrent--secondary--foreground--selected'); } } } } .torrent { - &__detail { flex: 0 0 auto; font-size: 0.8em; @@ -650,7 +538,6 @@ $more-info--border: $textbox-repeater--button--border; } &--percentComplete { - .icon { opacity: 1; } @@ -659,9 +546,7 @@ $more-info--border: $textbox-repeater--button--border; } .progress-bar { - &__icon { - .icon { margin-right: 0; } @@ -670,11 +555,9 @@ $more-info--border: $textbox-repeater--button--border; } &__more-info { - background: $more-info--background; + @include theme('background', 'more-info--background'); border-radius: 32px 0 0 32px; - box-shadow: - 0 0 30px rgba($more-info--box-shadow--hue, 0.11), - 0 0 0 1px rgba($more-info--box-shadow--hue, 0.07); + @include theme('box-shadow', 'more-info--box-shadow'); height: 32px; margin-top: -16px; position: absolute; @@ -696,10 +579,8 @@ $more-info--border: $textbox-repeater--button--border; } &:hover { - background: $more-info--background--hover; - box-shadow: - 0 0 30px rgba($more-info--box-shadow--hue, 0.2), - 0 0 0 1px rgba($more-info--box-shadow--hue, 0.1); + @include theme('background', 'more-info--background--hover'); + @include theme('box-shadow', 'more-info--box-shadow--hover'); .icon { fill: $blue; diff --git a/client/src/sass/components/_torrents.scss b/client/src/sass/components/_torrents.scss index 49d8d8f27..4f2304614 100644 --- a/client/src/sass/components/_torrents.scss +++ b/client/src/sass/components/_torrents.scss @@ -1,14 +1,10 @@ -$torrent-list--background: #fff; -$torrent-list--border: rgba($sidebar--background, 0.15); - -$torrents-list--alert--foreground: rgba(#8899a8, 0.5); - .torrents { - background: $torrent-list--background; - box-shadow: -1px 0 0 0 $torrent-list--border; + @include theme('background', 'torrent-list--background'); + @include theme('box-shadow', 'torrent-list--border'); display: flex; - flex: 1 1 auto; + flex: 1 1 0px; flex-direction: column; + overflow: hidden; position: relative; .loading-indicator { @@ -19,7 +15,7 @@ $torrents-list--alert--foreground: rgba(#8899a8, 0.5); } &__alert { - color: $torrents-list--alert--foreground; + @include theme('color', 'torrents-list--alert--foreground'); text-align: center; &__wrapper { @@ -42,8 +38,6 @@ $torrents-list--alert--foreground: rgba(#8899a8, 0.5); .torrent { &__list { - position: relative; - &__scrollbars { &--horizontal { left: 0; @@ -57,11 +51,16 @@ $torrents-list--alert--foreground: rgba(#8899a8, 0.5); } } + &__viewport { + overflow-y: overlay !important; + } + &__wrapper { display: flex; flex: 1 1 auto; flex-direction: column; height: 100%; + width: 100%; justify-content: center; list-style: none; opacity: 1; @@ -69,6 +68,8 @@ $torrents-list--alert--foreground: rgba(#8899a8, 0.5); position: relative; transition: opacity 1s; user-select: none; + -webkit-user-select: none; + -webkit-touch-callout: none; z-index: 2; } @@ -100,11 +101,10 @@ $torrents-list--alert--foreground: rgba(#8899a8, 0.5); .view { &--torrent-list { - background: #e9eef2; + @include theme('background', 'torrent-view--background'); box-shadow: -1px 0 $torrent-list--border; display: flex; flex-direction: column; - flex: 1; flex: 0 1 100%; } } diff --git a/client/src/sass/components/_transfer-data.scss b/client/src/sass/components/_transfer-data.scss index f8b487167..c1a97c497 100644 --- a/client/src/sass/components/_transfer-data.scss +++ b/client/src/sass/components/_transfer-data.scss @@ -2,7 +2,6 @@ $transfer-data--download: $green; $transfer-data--upload: $blue; .transfer-data { - &--download { color: $transfer-data--download; diff --git a/client/src/sass/style.scss b/client/src/sass/style.scss index b37016703..5cf7f6967 100644 --- a/client/src/sass/style.scss +++ b/client/src/sass/style.scss @@ -1,54 +1,57 @@ -@import "~ress"; -@import "~flood-ui-kit/dist/index"; +@import '~ress'; -@import "tools/variables"; -@import "tools/reset"; +@import 'tools/themes'; -@import "base/animations"; -@import "base/font-families"; -@import "base/layout"; -@import "base/main"; -@import "base/typography"; +@import 'ui/index'; -@import "components/action-bar"; -@import "components/app-wrapper"; -@import "components/alerts"; -@import "components/attached-panel"; -@import "components/badge"; -@import "components/base-menu"; -@import "components/client-stats"; -@import "components/connection-status"; -@import "components/dependency-list"; -@import "components/directory-tree"; -@import "components/dropdown"; -@import "components/dropzone"; -@import "components/duration"; -@import "components/filesystem"; -@import "components/floating-action"; -@import "components/icons"; -@import "components/interactive-list"; -@import "components/loading-indicator"; -@import "components/mediainfo"; -@import "components/modals"; -@import "components/notifications"; -@import "components/peers-list"; -@import "components/priority-meter"; -@import "components/progress-bar"; -@import "components/scrollbars"; -@import "components/search"; -@import "components/sidebar"; -@import "components/sidebar-filter"; -@import "components/sort-dropdown"; -@import "components/sortable-list"; -@import "components/table"; -@import "components/tags"; -@import "components/textbox-repeater"; -@import "components/toolbar"; -@import "components/tooltip"; -@import "components/torrent-details-panel"; -@import "components/torrents"; -@import "components/torrent"; -@import "components/transfer-data"; +@import 'tools/variables'; +@import 'tools/reset'; -@import "views/login"; -@import "views/feeds"; \ No newline at end of file +@import 'base/animations'; +@import 'base/font-families'; +@import 'base/layout'; +@import 'base/main'; +@import 'base/typography'; + +@import 'components/action-bar'; +@import 'components/app-wrapper'; +@import 'components/alerts'; +@import 'components/attached-panel'; +@import 'components/badge'; +@import 'components/base-menu'; +@import 'components/client-stats'; +@import 'components/connection-status'; +@import 'components/dependency-list'; +@import 'components/directory-tree'; +@import 'components/dropdown'; +@import 'components/dropzone'; +@import 'components/duration'; +@import 'components/filesystem'; +@import 'components/floating-action'; +@import 'components/icons'; +@import 'components/interactive-list'; +@import 'components/loading-indicator'; +@import 'components/mediainfo'; +@import 'components/modals'; +@import 'components/notifications'; +@import 'components/peers-list'; +@import 'components/priority-meter'; +@import 'components/progress-bar'; +@import 'components/scrollbars'; +@import 'components/search'; +@import 'components/sidebar'; +@import 'components/sidebar-filter'; +@import 'components/sort-dropdown'; +@import 'components/sortable-list'; +@import 'components/table'; +@import 'components/tags'; +@import 'components/textbox-repeater'; +@import 'components/toolbar'; +@import 'components/tooltip'; +@import 'components/torrent-details-panel'; +@import 'components/torrents'; +@import 'components/torrent'; +@import 'components/transfer-data'; + +@import 'views/login'; +@import 'views/feeds'; diff --git a/client/src/sass/style.scss.d.ts b/client/src/sass/style.scss.d.ts deleted file mode 100644 index 3ac9f0f8f..000000000 --- a/client/src/sass/style.scss.d.ts +++ /dev/null @@ -1,527 +0,0 @@ -declare const styles: { - readonly button: string; - readonly 'button--primary': string; - readonly inverse: string; - readonly 'button--secondary': string; - readonly 'button--tertiary': string; - readonly 'button--quaternary': string; - readonly 'button--is-disabled': string; - readonly button__content: string; - readonly icon: string; - readonly 'icon--loading': string; - readonly 'button--is-loading': string; - readonly 'context-menu': string; - readonly 'context-menu--enter': string; - readonly 'context-menu__items': string; - readonly 'context-menu__items--is-up': string; - readonly 'context-menu--enter--active': string; - readonly 'context-menu--exit': string; - readonly 'context-menu--exit--active': string; - readonly 'context-menu__items__padding-surrogate': string; - readonly 'context-menu__items--match-trigger-width': string; - readonly 'context-menu__items--no-padding': string; - readonly 'context-menu__items--no-scrolling': string; - readonly container: string; - readonly error: string; - readonly 'error--is-loading': string; - readonly input: string; - readonly form__row: string; - readonly 'form__row--no-margin': string; - readonly 'form__row--group': string; - readonly 'form__row--align--start': string; - readonly 'form__row--align--center': string; - readonly 'form__row--align--end': string; - readonly 'form__row--justify--start': string; - readonly 'form__row--justify--center': string; - readonly 'form__row--justify--end': string; - readonly form__row__item: string; - readonly 'is-first': string; - readonly 'is-last': string; - readonly 'form__row__item--grow': string; - readonly 'form__row__item--shrink': string; - readonly 'form__row__item--one-eighth': string; - readonly 'form__row__item--one-quarter': string; - readonly 'form__row__item--three-eighths': string; - readonly 'form__row__item--one-half': string; - readonly 'form__row__item--five-eighths': string; - readonly 'form__row__item--three-quarters': string; - readonly 'form__row__item--seven-eighths': string; - readonly checkbox: string; - readonly form__element__wrapper: string; - readonly radio: string; - readonly form__element__label: string; - readonly 'form__element--label-offset': string; - readonly 'form__element--match-textbox-height': string; - readonly 'form__element--has-addon--placed-before': string; - readonly 'form__element--has-addon--count-2': string; - readonly 'form__element--has-addon--placed-after': string; - readonly form__element__addon: string; - readonly form__element: string; - readonly 'icon--stroke': string; - readonly 'form__element__addon--placed-before': string; - readonly 'form__element__addon--index-2': string; - readonly 'form__element__addon--placed-after': string; - readonly 'form__element__addon--is-icon': string; - readonly 'form__element__addon--is-interactive': string; - readonly 'icon--small': string; - readonly 'icon--large': string; - readonly icon__element: string; - readonly 'icon--loading--ring': string; - readonly 'icon__ring-slice': string; - readonly 'input--hidden': string; - readonly 'toggle-input': string; - readonly 'toggle-input__indicator': string; - readonly 'toggle-input__indicator__icon': string; - readonly 'toggle-input--is-active': string; - readonly 'toggle-input__element': string; - readonly overlay: string; - readonly 'overlay--transparent': string; - readonly 'overlay--no-interaction': string; - readonly panel: string; - readonly 'panel--medium': string; - readonly panel__content: string; - readonly panel__header: string; - readonly 'panel__header--has-border': string; - readonly panel__footer: string; - readonly 'panel__footer--has-border': string; - readonly 'panel--large': string; - readonly h1: string; - readonly h2: string; - readonly h3: string; - readonly h4: string; - readonly h5: string; - readonly h6: string; - readonly 'panel__content--has-border--top': string; - readonly 'panel__content--has-border--bottom': string; - readonly 'panel--light': string; - readonly portal: string; - readonly section: string; - readonly section__heading: string; - readonly padded: string; - readonly select: string; - readonly select__button: string; - readonly select__indicator: string; - readonly select__item: string; - readonly 'select__item--is-selected': string; - readonly 'select--is-open': string; - readonly app: string; - readonly application: string; - readonly application__view: string; - readonly application__content: string; - readonly application__panel: string; - readonly 'application__panel--torrent-list': string; - readonly 'is-open': string; - readonly 'application__panel--torrent-details': string; - readonly unit: string; - readonly 'text-overflow': string; - readonly 'copy--lead': string; - readonly 'action-bar': string; - readonly 'action-bar--is-condensed': string; - readonly 'action-bar__item': string; - readonly 'action-bar__item--sort-torrents': string; - readonly dropdown: string; - readonly dropdown__content: string; - readonly 'action-bar__item--torrent-operations': string; - readonly 'action-bar__group': string; - readonly 'action-bar__group--has-divider': string; - readonly actions: string; - readonly action: string; - readonly action__label: string; - readonly 'application__loading-overlay': string; - readonly 'application__loading-overlay-exit': string; - readonly 'application__loading-overlay-exit-active': string; - readonly 'application__entry-barrier': string; - readonly alerts__list: string; - readonly 'alerts__list-exit': string; - readonly 'alerts__list-exit-active': string; - readonly 'alerts__list-enter': string; - readonly 'alerts__list-enter-active': string; - readonly alert: string; - readonly 'is-success': string; - readonly alert__count: string; - readonly 'is-error': string; - readonly alert__content: string; - readonly 'attached-panel': string; - readonly 'attached-panel__content': string; - readonly 'attached-panel__wrapper': string; - readonly 'attached-panel-enter': string; - readonly 'attached-panel-enter-active': string; - readonly 'attached-panel-exit': string; - readonly 'attached-panel-exit-active': string; - readonly 'textbox--has-attached-panel--is-open': string; - readonly badge: string; - readonly menu: string; - readonly menu__item: string; - readonly 'menu__item__label--primary': string; - readonly 'has-action': string; - readonly menu__item__label: string; - readonly menu__item__label__action: string; - readonly 'menu__item__label--secondary': string; - readonly 'menu__item--separator': string; - readonly 'is-selectable': string; - readonly 'is-selected': string; - readonly 'menu-enter': string; - readonly 'fade-in': string; - readonly 'menu-exit': string; - readonly 'fade-out': string; - readonly 'client-stats': string; - readonly 'client-stats__rates': string; - readonly 'client-stats__rate': string; - readonly 'client-stats__rate--download': string; - readonly 'client-stats__rate__data--limit': string; - readonly 'client-stats__rate--upload': string; - readonly 'client-stats__rate__icon': string; - readonly 'client-stats__rate__data--secondary': string; - readonly 'client-stats__rate__data--timestamp': string; - readonly 'client-stats__rate__data--primary': string; - readonly 'is-visible': string; - readonly 'client-stats__graph': string; - readonly 'loading-indicator': string; - readonly 'graph__gradient--bottom': string; - readonly 'graph__gradient--bottom--upload': string; - readonly 'graph__gradient--bottom--download': string; - readonly 'graph__gradient--top': string; - readonly 'graph__gradient--top--upload': string; - readonly 'graph__gradient--top--download': string; - readonly graph__area: string; - readonly graph__line: string; - readonly 'graph__line--upload': string; - readonly 'graph__line--download': string; - readonly graph__circle: string; - readonly 'graph__circle--upload': string; - readonly 'graph__circle--download': string; - readonly 'connection-status': string; - readonly 'connection-status__icon': string; - readonly 'connection-status__copy': string; - readonly 'dependency-list': string; - readonly 'dependency-list__dependency': string; - readonly 'dependency-list__dependency__icon': string; - readonly 'dependency-list__dependency--satisfied': string; - readonly 'directory-tree': string; - readonly 'directory-tree__wrapper': string; - readonly 'directory-tree__wrapper--toolbar-visible': string; - readonly 'directory-tree__selection-toolbar': string; - readonly 'modal__content--nested-scroll__content': string; - readonly 'directory-tree__selection-toolbar__item': string; - readonly 'directory-tree__selection-toolbar__item-count': string; - readonly 'button--download': string; - readonly dropdown__items: string; - readonly dropdown__trigger: string; - readonly dropdown__button: string; - readonly dropdown__value: string; - readonly 'directory-tree__parent-directory': string; - readonly 'icon--disk': string; - readonly 'directory-tree__checkbox': string; - readonly checkbox__decoy: string; - readonly 'directory-tree__tree': string; - readonly 'directory-tree__node': string; - readonly file__label: string; - readonly 'directory-tree__checkbox__item--icon': string; - readonly 'directory-tree__checkbox__item--checkbox': string; - readonly 'directory-tree__node--selected': string; - readonly 'directory-tree__node--directory': string; - readonly 'directory-tree__node--group': string; - readonly 'directory-tree__node--file-list': string; - readonly file: string; - readonly 'icon--file': string; - readonly file__detail: string; - readonly 'file__detail--secondary': string; - readonly 'file__detail--priority': string; - readonly 'directory-tree__checkbox__item': string; - readonly 'icon--folder': string; - readonly file__checkbox: string; - readonly file__name: string; - readonly 'dropdown--direction-up': string; - readonly dropdown__content__container: string; - readonly dropdown__label: string; - readonly dropdown__header: string; - readonly dropdown__item: string; - readonly dropdown__list: string; - readonly 'dropdown--align-right': string; - readonly 'dropdown--match-button-width': string; - readonly 'dropdown--width-small': string; - readonly 'is-expanded': string; - readonly dropzone: string; - readonly dropzone__icon: string; - readonly 'dropzone--is-dragging': string; - readonly 'icon--files': string; - readonly 'icon--files__file--right': string; - readonly 'icon--files__file--left': string; - readonly dropzone__copy: string; - readonly 'dropzone__browse-button': string; - readonly 'dropzone__selected-files': string; - readonly 'interactive-list': string; - readonly 'dropzone__selected-files__file': string; - readonly 'dropzone--with-overlay': string; - readonly dropzone__overlay: string; - readonly 'duration--segment': string; - readonly 'filesystem__directory-list': string; - readonly 'filesystem__directory-list__item': string; - readonly 'filesystem__directory-list__item--parent': string; - readonly 'filesystem__directory-list__item--directory': string; - readonly 'filesystem__directory-list__item--file': string; - readonly 'floating-action__button': string; - readonly 'floating-action__button--search': string; - readonly 'floating-action__group--on-textbox': string; - readonly 'icon--eta': string; - readonly icon__ring: string; - readonly 'icon--information__fill': string; - readonly 'icon--information__ring': string; - readonly 'icon--limits': string; - readonly 'limits__bars--top': string; - readonly 'limits__bars--middle': string; - readonly 'limits__bars--bottom': string; - readonly 'icon--loading-indicator': string; - readonly 'loading-indicator--dots__dot': string; - readonly 'loading-indicator-dots-pulse': string; - readonly 'loading-indicator--dots__dot--center': string; - readonly 'loading-indicator--dots__dot--right': string; - readonly 'icon--spinner': string; - readonly 'spinner-spin': string; - readonly 'interactive-list--loading': string; - readonly 'interactive-list__item': string; - readonly 'interactive-list__icon--action': string; - readonly 'interactive-list__icon--action--warning': string; - readonly 'interactive-list__item--stacked-content': string; - readonly 'interactive-list__label': string; - readonly 'interactive-list__label__text': string; - readonly 'interactive-list__label__tag': string; - readonly tag: string; - readonly 'interactive-list__loading-indicator': string; - readonly 'interactive-list__loading-indicator-enter': string; - readonly 'interactive-list__loading-indicator-enter-active': string; - readonly 'interactive-list__loading-indicator-exit': string; - readonly 'interactive-list__loading-indicator-exit-active': string; - readonly 'interactive-list__icon': string; - readonly 'icon--close': string; - readonly 'interactive-list__detail--primary': string; - readonly 'interactive-list__detail--tertiary': string; - readonly 'interactive-list__detail-list': string; - readonly 'interactive-list__detail-list__item': string; - readonly 'interactive-list__detail-list__item--overflow': string; - readonly 'is-inverse': string; - readonly 'loading-indicator__bar': string; - readonly 'loading-indicator-swipe': string; - readonly 'loading-indicator__bar--1': string; - readonly 'loading-indicator__bar--2': string; - readonly 'loading-indicator__bar--3': string; - readonly mediainfo: string; - readonly mediainfo__toolbar: string; - readonly tooltip__wrapper: string; - readonly 'mediainfo__copy-button': string; - readonly mediainfo__output: string; - readonly modal: string; - readonly modal__overlay: string; - readonly 'modal--align-center': string; - readonly modal__tabs: string; - readonly modal__tab: string; - readonly 'is-active': string; - readonly modal__header: string; - readonly 'modal--tabs-in-header': string; - readonly 'has-tabs': string; - readonly modal__content: string; - readonly modal__content__wrapper: string; - readonly 'modal__content--nested-scroll': string; - readonly 'modal__content--nested-scroll__header': string; - readonly modal__body: string; - readonly 'modal--tabs-in-body': string; - readonly modal__footer: string; - readonly modal__actions: string; - readonly 'modal__button-group': string; - readonly 'modal__animation-enter': string; - readonly 'modal__animation-enter-active': string; - readonly 'modal__animation-exit': string; - readonly 'modal__animation-exit-active': string; - readonly 'modal--vertical': string; - readonly 'modal--size-large': string; - readonly form__section__heading: string; - readonly 'form__section__sub-heading': string; - readonly 'notifications--empty': string; - readonly 'notifications--is-loading': string; - readonly notifications__list: string; - readonly 'notifications__loading-indicator': string; - readonly notifications__badge: string; - readonly notifications__list__item: string; - readonly notifications__toolbar: string; - readonly 'toolbar__item--button': string; - readonly notification__heading: string; - readonly notification__category: string; - readonly 'notification__message__sub-heading': string; - readonly 'peers-list__flag': string; - readonly 'peers-list__flag__image': string; - readonly 'peers-list__flag__text': string; - readonly 'peers-list__encryption': string; - readonly 'priority-meter': string; - readonly 'priority-meter__wrapper': string; - readonly 'priority-meter--max-2': string; - readonly 'priority-meter--level-0': string; - readonly 'priority-meter--level-1': string; - readonly 'priority-meter--level-2': string; - readonly 'priority-meter--max-3': string; - readonly 'priority-meter--level-3': string; - readonly 'progress-bar': string; - readonly 'progress-bar__icon': string; - readonly 'torrent--is-seeding': string; - readonly 'torrent--is-stopped': string; - readonly 'torrent--has-error': string; - readonly 'torrent--is-checking': string; - readonly 'torrent--is-selected': string; - readonly 'progress-bar__fill': string; - readonly 'progress-bar__fill__wrapper': string; - readonly 'candy-stripe': string; - readonly scrollbars__thumb: string; - readonly 'scrollbars__thumb--surrogate': string; - readonly 'is-inverted': string; - readonly scrollbars: string; - readonly search: string; - readonly textbox: string; - readonly 'is-in-use': string; - readonly application__sidebar: string; - readonly 'sidebar__icon-button': string; - readonly 'sidebar__icon-button--interactive': string; - readonly 'sidebar__action--last': string; - readonly sidebar__actions: string; - readonly sidebar__diskusage: string; - readonly 'diskuage__size-avail': string; - readonly 'diskusage__text-row': string; - readonly 'diskusage__details-list': string; - readonly 'diskusage__details-list__item': string; - readonly 'diskusage__details-list__label': string; - readonly 'dropdown--speed-limits': string; - readonly 'sidebar-filter': string; - readonly 'sidebar-filter__item': string; - readonly 'sidebar-filter__item--heading': string; - readonly 'sort-dropdown__item': string; - readonly 'sort-dropdown__indicator': string; - readonly 'sort-dropdown__indicator--asc': string; - readonly 'sortable-list': string; - readonly 'sortable-list__item': string; - readonly 'sortable-list__item--is-dragging': string; - readonly 'sortable-list__item--is-locked': string; - readonly 'sortable-list__item--is-preview': string; - readonly 'icon--error': string; - readonly 'icon--lock': string; - readonly 'sortable-list__content': string; - readonly 'sortable-list__content__wrapper': string; - readonly 'sortable-list__content--primary': string; - readonly 'sortable-list__content--secondary': string; - readonly 'sortable-list__content--secondary__copy': string; - readonly 'table__row--heading': string; - readonly table__heading: string; - readonly 'table__heading--is-sorted': string; - readonly 'table__heading--direction--asc': string; - readonly 'table__heading--fill': string; - readonly table__heading__handle: string; - readonly table__heading__label: string; - readonly 'table__heading__resize-line': string; - readonly 'table__heading__column-fill': string; - readonly table__cell: string; - readonly 'textbox-repeater': string; - readonly toolbar: string; - readonly 'toolbar--dark': string; - readonly 'toolbar--bottom': string; - readonly 'toolbar--top': string; - readonly toolbar__item: string; - readonly 'is-disabled': string; - readonly 'toolbar__item--centered': string; - readonly 'toolbar__item--label': string; - readonly tooltip: string; - readonly tooltip__content: string; - readonly 'tooltip__content--no-padding': string; - readonly 'tooltip__content--padding-surrogate': string; - readonly 'is-interactive': string; - readonly 'tooltip--no-wrap': string; - readonly 'tooltip--position--bottom': string; - readonly 'tooltip--anchor--center': string; - readonly 'tooltip--position--top': string; - readonly 'tooltip--anchor--start': string; - readonly 'tooltip--align--center': string; - readonly 'tooltip--anchor--end': string; - readonly 'tooltip--position--left': string; - readonly 'tooltip--position--right': string; - readonly 'tooltip--is-error': string; - readonly 'torrent-details__heading': string; - readonly 'torrent-details__sub-heading': string; - readonly 'torrent-details__sub-heading__secondary': string; - readonly 'torrent-details__sub-heading__tertiary': string; - readonly 'torrent-details__header': string; - readonly 'is-completed': string; - readonly 'is-stopped': string; - readonly 'torrent-details__action': string; - readonly 'torrent-details__table': string; - readonly 'torrent-details__table__heading--primary': string; - readonly 'torrent-details__table__heading--secondary': string; - readonly 'torrent-details__table__heading--tertiary': string; - readonly 'torrent-details__section': string; - readonly 'torrent-details__section__heading': string; - readonly 'torrent-details__section__null-data': string; - readonly 'torrent-details__section--file-tree': string; - readonly 'directory-tree__node--selectable': string; - readonly 'file__detail--size': string; - readonly 'torrent-details__detail--hash': string; - readonly 'torrent-details__detail__value': string; - readonly 'torrent-details__detail--tags': string; - readonly 'torrent-details__detail': string; - readonly 'torrent-details__table__heading': string; - readonly 'torrent-details__detail__label': string; - readonly 'not-available': string; - readonly torrents: string; - readonly torrents__alert: string; - readonly torrents__alert__wrapper: string; - readonly torrents__alert__action: string; - readonly torrent__list: string; - readonly 'torrent__list__scrollbars--horizontal': string; - readonly 'torrent__list__scrollbars--vertical': string; - readonly torrent__list__wrapper: string; - readonly 'torrent__list--loading-enter': string; - readonly 'torrent__list--loading-enter-active': string; - readonly 'torrent__list--loading-exit': string; - readonly 'torrent__list--loading-exit-active': string; - readonly 'torrent__list--empty': string; - readonly 'view--torrent-list': string; - readonly torrent: string; - readonly 'torrent__more-info': string; - readonly torrent__detail: string; - readonly 'torrent__detail--name': string; - readonly 'torrent__detail--tags': string; - readonly torrent__tag: string; - readonly 'torrent__detail__icon--checkmark': string; - readonly 'torrent__details__section--secondary': string; - readonly 'torrent__details__section--tertiary': string; - readonly 'torrent--is-downloading--actively': string; - readonly 'torrent__detail--downRate': string; - readonly 'torrent-details__sub-heading__tertiary--download': string; - readonly 'torrent--is-uploading--actively': string; - readonly 'torrent__detail--upRate': string; - readonly 'torrent-details__sub-heading__tertiary--upload': string; - readonly 'torrent--is-expanded': string; - readonly 'torrent__detail--eta': string; - readonly torrent__details__section: string; - readonly 'torrent__details__section--quaternary': string; - readonly 'torrent__details__section--primary': string; - readonly torrent__details__section__wrapper: string; - readonly 'torrent__detail--percentComplete': string; - readonly 'torrent__detail--upTotal': string; - readonly 'torrent__detail--sizeBytes': string; - readonly 'torrent__detail--freeDiskSpace': string; - readonly 'torrent__detail--added': string; - readonly 'torrent__detail--creationDate': string; - readonly 'torrent__detail--isPrivate': string; - readonly 'torrent__detail--peers': string; - readonly 'torrent__detail--ratio': string; - readonly 'torrent__detail--seeds': string; - readonly torrent__tags: string; - readonly 'torrent--is-condensed': string; - readonly 'transfer-data--download': string; - readonly 'transfer-data--upload': string; - readonly 'application__view--auth-form': string; - readonly 'form--authentication': string; - readonly form__wrapper: string; - readonly form__header: string; - readonly form__label: string; - readonly 'form__row--error': string; - readonly form__actions: string; - readonly 'feed-list__feed-label': string; - readonly rotateAroundMidpoint: string; -}; -export = styles; diff --git a/client/src/sass/tools/_colors-dark.scss b/client/src/sass/tools/_colors-dark.scss new file mode 100644 index 000000000..09b4feedc --- /dev/null +++ b/client/src/sass/tools/_colors-dark.scss @@ -0,0 +1,26 @@ +$md-surface-dark: #121212; +$md-surface-dark-e01: #1d1d1d; +$md-surface-dark-e02: #212121; +$md-surface-dark-e03: #242424; +$md-surface-dark-e04: #262626; +$md-surface-dark-e06: #2c2c2c; +$md-surface-dark-e08: #2d2d2d; +$md-surface-dark-e12: #323232; +$md-surface-dark-e16: #353535; +$md-surface-dark-e24: #373737; + +$md-grey-50: #fafafa; +$md-grey-100: #f5f5f5; +$md-grey-200: #eeeeee; +$md-grey-300: #e0e0e0; +$md-grey-400: #bdbdbd; +$md-grey-500: #9e9e9e; +$md-grey-600: #757575; +$md-grey-700: #616161; +$md-grey-800: #424242; +$md-grey-900: #212121; + +$md-text-primary-dark: rgba(255, 255, 255, 0.87); +$md-text-secondary-dark: rgba(255, 255, 255, 0.6); +$md-text-disabled-dark: rgba(255, 255, 255, 0.38); +$md-text-dividers-dark: rgba(255, 255, 255, 0.12); diff --git a/client/src/sass/tools/_colors.scss b/client/src/sass/tools/_colors.scss index 042adcc52..0550ed01a 100644 --- a/client/src/sass/tools/_colors.scss +++ b/client/src/sass/tools/_colors.scss @@ -1,17 +1,71 @@ +@function harden($color, $modifier: 1) { + @return darken($color, 6% * $modifier); +} + +$white: #fff; +$black: #000; + +$red: #f34570; +$red--alpha--light: rgba($red, 0.1); +$red--alpha--medium: rgba($red, 0.2); + +$blue: #349cf4; +$blue--light: lighten($blue, 7%); +$blue--lighter: lighten($blue, 40%); +$blue--soft: rgba($blue, 0.05); +$blue--hard: harden($blue, 1.5); +$blue--darker: darken($blue, 20%); + +$light-blue: #e9eef2; $dark-blue: #1d2938; -$grey: #293341; -$light-grey: #3A4553; -$light-grey--lighter: lighten($light-grey, 40%); -$blue: #258de5; -$blue--lighter: saturate(lighten($blue, 10%), 100%); + $green: #39ce83; +$green--light: lighten($green, 7%); +$green--hard: harden($green, 1.3); $green--darker: darken(#39ce83, 4%); -$light-blue: #e9eef2; -$red: #e95779; -$white: #fff; + +$medium-grey: #abbac7; +$medium-grey--lighter: #b9c7d3; +$medium-grey--soft: rgba($medium-grey, 0.15); + +$grey: #8899a8; +$grey--soft: rgba($grey, 0.15); +$grey--light: lighten($grey, 7%); +$grey--lighter: lighten($grey, 25%); +$grey--hard: harden($grey, 1.75); +$grey--harder: harden($grey, 1); + +$dark-grey: #34516c; +$dark-grey--light: lighten($dark-grey, 7%); +$darker-grey: #1d2938; +$darkest-grey: #28303b; +$darkest-grey--hard: #293341; +$darkest-grey--darker: #202d3c; + +$light-grey: #e9eef2; +$light-grey--lighter: #f8f9fb; +$light-grey--soft: rgba($light-grey, 0.15); +$light-grey--hard: harden($light-grey); + +$another-grey: #3a4553; $background: $dark-blue; $foreground: #53718a; -$sidebar--background: $grey; +$sidebar--background: $darkest-grey--hard; +$sidebar--foreground: #526780; $main-content--background: $light-blue; + +$textbox--background: #242b36; +$textbox--foreground: #5e728c; +$textbox--border: #1a2028; +$textbox--fulfilled--background: $textbox--background; +$textbox--placeholder: #424d5e; +$textbox--selection--foreground: #1a2028; +$textbox--selection--background: $blue--lighter; +$textbox--active--background: $textbox--background; +$textbox--active--border: $blue; +$textbox--active--foreground: $blue; +$textbox--active--placeholder: $textbox--placeholder; + +$table--heading--color: #abbac7; diff --git a/client/src/sass/tools/_themes.scss b/client/src/sass/tools/_themes.scss new file mode 100644 index 000000000..d2e107fcf --- /dev/null +++ b/client/src/sass/tools/_themes.scss @@ -0,0 +1,222 @@ +@import 'colors'; +@import 'colors-dark'; + +@mixin themeProperty($theme, $property, $color, $additionalPropertiesPositionIsFront, $additionalProperties) { + @if $additionalPropertiesPositionIsFront { + #{$property}: unquote($additionalProperties + ' ' + map-get($theme, $color)); + } @else { + #{$property}: unquote(map-get($theme, $color) + ' ' + $additionalProperties); + } +} + +@mixin theme($property, $color, $additionalPropertiesPositionIsFront: false, $additionalProperties: '') { + $light: map-get($themes, 'light'); + $dark: map-get($themes, 'dark'); + + @include themeProperty($light, $property, $color, $additionalPropertiesPositionIsFront, $additionalProperties); + + .dark & { + @include themeProperty($dark, $property, $color, $additionalPropertiesPositionIsFront, $additionalProperties); + } +} + +$themes: ( + light: ( + torrent-view--background: #e9eef2, + torrent-list--background: #fff, + torrent-list--border: -1px 0 0 0 rgba($sidebar--background, 0.15), + torrents-list--alert--foreground: rgba(#8899a8, 0.5), + action--foreground: #8899a8, + table--heading--background: rgba(#f6f8fa, 0.6), + table--heading--color: $table--heading--color, + table--heading--color--hover: darken($table--heading--color, 10%), + table--heading--color--active: darken($table--heading--color, 30%), + table--heading--color--active--hover: darken($table--heading--color, 40%), + table--heading--resize--indicator--background: rgba(darken(#f6f8fa, 10%), 0.75), + table--heading--border: 0 1px 0 rgba(#1d2938, 0.1), + table--heading--border--horizontal: 1px solid rgba(#1d2938, 0.08), + torrent--primary--font-weight: 500, + torrent--primary--foreground: saturate(darken($grey, 20%), 10%), + torrent--primary--foreground--error: $red, + torrent--primary--foreground--error--stopped: rgba($red, 0.6), + torrent--primary--foreground--error--stopped--selected: rgba($white, 0.6), + torrent--primary--foreground--stopped: rgba($grey, 0.8), + torrent--primary--foreground--selected: $white, + torrent--primary--foreground--selected--stopped: rgba($white, 0.6), + torrent--primary--foreground--selected--error: $white, + torrent--secondary--foreground: #8899a8, + torrent--secondary--foreground--stopped: #c3ccd3, + torrent--secondary--foreground--selected: $white, + torrent--secondary--foreground--selected--stopped: rgba($white, 0.6), + torrent--tertiary--foreground: #8899a8, + torrent--tertiary--foreground--stopped: #c3ccd3, + torrent--tertiary--foreground--selected: rgba($white, 0.9), + torrent--tertiary--foreground--selected--stopped: rgba($white, 0.5), + torrent--background--hover: #f6f8fa, + torrent--background--selected: $blue, + torrent--background--error: $red, + torrent--border: 1px solid #e9eef2, + torrent--border--selected: darken($blue, 3%), + torrent--border--selected--error: darken($red, 3%), + torrent--speed--foreground--selected--has-error: $white, + torrent--speed--foreground--selected: $white, + torrent--tags--background: #dce2e6, + torrent--tags--foreground: #6f7d87, + torrent--tags--background--selected: #4ea3ea, + torrent--tags--background--selected--has-error: #ed7994, + torrent--tags--foreground--selected: $white, + torrent--tags--foreground--has-error: #fbe8ec, + more-info--background: $white, + more-info--background--hover: $white, + more-info--box-shadow: ( + 0 0 30px rgba(#1a2f3d, 0.11), + 0 0 0 1px rgba(#1a2f3d, 0.07), + ), + more-info--box-shadow--hover: ( + 0 0 30px rgba(#1a2f3d, 0.2), + 0 0 0 1px rgba(#1a2f3d, 0.1), + ), + progress-bar--fill: $green, + progress-bar--fill--checking: #8899a8, + progress-bar--fill--completed: $blue, + progress-bar--fill--error: #e95779, + progress-bar--fill--error--stopped: #f2acbc, + progress-bar--fill--selected: #fff, + progress-bar--fill--selected--stopped: #5daaeb, + progress-bar--fill--stopped: #e7ebee, + progress-bar--track--background: rgba($green, 0.15), + progress-bar--track--background--completed: rgba($blue, 0.15), + progress-bar--track--background--error: rgba(#e95779, 0.15), + progress-bar--track--background--error--stopped: rgba(#f2acbc, 0.15), + progress-bar--track--background--selected: rgba(#fff, 0.15), + progress-bar--track--background--selected--stopped: rgba(#fff, 0.15), + progress-bar--track--background--stopped: rgba(#e7ebee, 0.35), + progress-bar--track--background--checking: + linear-gradient( + -45deg, + rgba(rgba(#8899a8, 0.15), 0) 0, + rgba(rgba(#8899a8, 0.15), 0) 25%, + rgba(rgba(#8899a8, 0.15), 0.5) 25%, + rgba(rgba(#8899a8, 0.15), 0.5) 50%, + rgba(rgba(#8899a8, 0.15), 0) 50%, + rgba(rgba(#8899a8, 0.15), 0) 75%, + rgba(rgba(#8899a8, 0.15), 0.5) 75%, + rgba(rgba(#8899a8, 0.15), 0.5) 100% + ), + sidebar--background: $sidebar--background, + sidebar--foreground: $sidebar--foreground, + sidebar--border: 1px 0 rgba(darken($sidebar--foreground, 40%), 0.3), + sidebar-filter--foreground: $sidebar--foreground, + sidebar-filter--foreground--fill: rgba($sidebar--foreground, 0.5), + sidebar-filter--foreground--header: rgba($sidebar--foreground, 0.5), + sidebar-filter--foreground--active: $blue, + sidebar-filter--foreground--hover: lighten($sidebar--foreground, 15%), + sidebar--icon-button--fill: $sidebar--foreground, + sidebar--icon-button--fill--hover: $blue, + sidebar--icon-button--foreground: rgba($sidebar--foreground, 0.7), + sidebar--icon-button--foreground--hover: $blue, + sidebar-filter--count--foreground: darken($darkest-grey--hard, 5%), + sidebar-filter--count--foreground--active: darken($darkest-grey--hard, 5%), + sidebar-filter--count--background: lighten($darkest-grey--hard, 20%), + sidebar-filter--count--background--active: $blue, + ), + dark: ( + torrent-view--background: $md-surface-dark-e08, + torrent-list--background: $md-surface-dark, + torrent-list--border: -1px 0 0 0 rgba($sidebar--background, 0.15), + torrents-list--alert--foreground: rgba(#8899a8, 0.5), + action--foreground: $md-grey-500, + table--heading--background: $md-surface-dark-e01, + table--heading--color: $md-text-disabled-dark, + table--heading--color--hover: $md-text-secondary-dark, + table--heading--color--active: $md-text-primary-dark, + table--heading--color--active--hover: $white, + table--heading--resize--indicator--background: $md-text-secondary-dark, + table--heading--border: 0 1px 0 $md-text-dividers-dark, + table--heading--border--horizontal: 1px solid $md-text-dividers-dark, + torrent--primary--font-weight: 400, + torrent--primary--foreground: $md-text-primary-dark, + torrent--primary--foreground--error: $red, + torrent--primary--foreground--error--stopped: rgba($red, 0.6), + torrent--primary--foreground--error--stopped--selected: rgba($white, 0.6), + torrent--primary--foreground--stopped: rgba($grey, 0.8), + torrent--primary--foreground--selected: $white, + torrent--primary--foreground--selected--stopped: rgba($white, 0.6), + torrent--primary--foreground--selected--error: $white, + torrent--secondary--foreground: $md-text-secondary-dark, + torrent--secondary--foreground--stopped: $md-text-disabled-dark, + torrent--secondary--foreground--selected: $white, + torrent--secondary--foreground--selected--stopped: rgba($white, 0.6), + torrent--tertiary--foreground: $md-text-secondary-dark, + torrent--tertiary--foreground--stopped: #c3ccd3, + torrent--tertiary--foreground--selected: rgba($white, 0.9), + torrent--tertiary--foreground--selected--stopped: rgba($white, 0.5), + torrent--background--hover: #f6f8fa, + torrent--background--selected: $blue, + torrent--background--error: $red, + torrent--border: 1px solid $md-text-dividers-dark, + torrent--border--selected: darken($blue, 3%), + torrent--border--selected--error: darken($red, 3%), + torrent--speed--foreground--selected--has-error: $white, + torrent--speed--foreground--selected: $white, + torrent--tags--background: $md-surface-dark-e06, + torrent--tags--foreground: $md-text-secondary-dark, + torrent--tags--background--selected: #4ea3ea, + torrent--tags--background--selected--has-error: #ed7994, + torrent--tags--foreground--selected: $white, + torrent--tags--foreground--has-error: #fbe8ec, + more-info--background: $white, + more-info--background--hover: $white, + more-info--box-shadow: ( + 0 0 30px rgba(#1a2f3d, 0.11), + 0 0 0 1px rgba(#1a2f3d, 0.07), + ), + more-info--box-shadow--hover: ( + 0 0 30px rgba(#1a2f3d, 0.2), + 0 0 0 1px rgba(#1a2f3d, 0.1), + ), + progress-bar--fill: $green, + progress-bar--fill--checking: #8899a8, + progress-bar--fill--completed: $blue, + progress-bar--fill--error: #e95779, + progress-bar--fill--error--stopped: #f2acbc, + progress-bar--fill--selected: #fff, + progress-bar--fill--selected--stopped: $md-text-secondary-dark, + progress-bar--fill--stopped: $md-text-disabled-dark, + progress-bar--track--background: rgba($green, 0.15), + progress-bar--track--background--completed: rgba($blue, 0.15), + progress-bar--track--background--error: rgba(#e95779, 0.15), + progress-bar--track--background--error--stopped: rgba(#f2acbc, 0.15), + progress-bar--track--background--selected: rgba(#fff, 0.15), + progress-bar--track--background--selected--stopped: $md-text-dividers-dark, + progress-bar--track--background--stopped: $md-text-dividers-dark, + progress-bar--track--background--checking: + linear-gradient( + -45deg, + rgba(rgba(#8899a8, 0.15), 0) 0, + rgba(rgba(#8899a8, 0.15), 0) 25%, + rgba(rgba(#8899a8, 0.15), 0.5) 25%, + rgba(rgba(#8899a8, 0.15), 0.5) 50%, + rgba(rgba(#8899a8, 0.15), 0) 50%, + rgba(rgba(#8899a8, 0.15), 0) 75%, + rgba(rgba(#8899a8, 0.15), 0.5) 75%, + rgba(rgba(#8899a8, 0.15), 0.5) 100% + ), + sidebar--background: $md-surface-dark, + sidebar--foreground: $md-text-secondary-dark, + sidebar--border: 1px 0 $md-text-dividers-dark, + sidebar-filter--foreground: $md-text-disabled-dark, + sidebar-filter--foreground--fill: $md-text-dividers-dark, + sidebar-filter--foreground--header: $md-text-primary-dark, + sidebar-filter--foreground--active: $blue, + sidebar-filter--foreground--hover: $md-text-secondary-dark, + sidebar--icon-button--fill: $md-grey-500, + sidebar--icon-button--fill--hover: $blue, + sidebar--icon-button--foreground: $md-grey-400, + sidebar--icon-button--foreground--hover: $blue, + sidebar-filter--count--foreground: $md-text-secondary-dark, + sidebar-filter--count--foreground--active: darken($darkest-grey--hard, 5%), + sidebar-filter--count--background: $md-surface-dark-e06, + sidebar-filter--count--background--active: $blue, + ), +); diff --git a/client/src/sass/tools/_variables.scss b/client/src/sass/tools/_variables.scss index 86712e800..95d2713ca 100644 --- a/client/src/sass/tools/_variables.scss +++ b/client/src/sass/tools/_variables.scss @@ -1,22 +1,9 @@ -@import 'colors'; - $font-family: 'Roboto', sans-serif; $spacing-unit: 25px; $torrent-details--width: 85%; -$textbox--background: #242b36; -$textbox--foreground: #5e728c; -$textbox--border: #1a2028; -$textbox--fulfilled--background: $textbox--background; $form--element--border-radius: 4px; $textbox--padding--vertical: 10px; $textbox--padding--horizontal: 15px; -$textbox--placeholder: #424d5e; -$textbox--selection--foreground: #1a2028; -$textbox--selection--background: $blue--lighter; -$textbox--active--background: $textbox--background; -$textbox--active--border: $blue; -$textbox--active--foreground: $blue; -$textbox--active--placeholder: $textbox--placeholder; diff --git a/client/src/sass/ui/components/button.scss b/client/src/sass/ui/components/button.scss new file mode 100644 index 000000000..63e489495 --- /dev/null +++ b/client/src/sass/ui/components/button.scss @@ -0,0 +1,285 @@ +@function button--text-shadow($color) { + @return 0 1px 0 darken($color, 5%), 0 1px 2px rgba(darken($color, 10%), 0.8); +} + +@function button--svg-shadow($color) { + @return drop-shadow(0 1px 0 darken($color, 5%)) drop-shadow(0 1px 2px rgba(darken($color, 10%), 0.8)); +} + +.button { + border: $form--element--border-width solid currentColor; + cursor: pointer; + color: $white; + font-weight: 500; + line-height: 1.5; + position: relative; + user-select: none; + + &:active { + color: rgba($white, 0.8); + } + + &--primary { + background: $blue; + border-color: $blue--hard; + box-shadow: form--element--box-shadow($blue); + text-shadow: button--text-shadow($blue--hard); + + &:hover { + background: $blue--light; + } + + &:active, + &:focus { + box-shadow: form--element--box-shadow--active($blue--hard); + } + + &:active { + background: $blue--hard; + border-color: darken($blue--hard, 5%); + box-shadow: form--element--box-shadow--active($blue--hard); + } + + .inverse & { + border-color: $blue; + + &:focus, + &:hover { + background: $blue--light; + border-color: $blue--light; + } + + &:active, + &:focus { + box-shadow: form--element--inverse--box-shadow--active($blue--light); + } + + &:active { + background: $blue; + border-color: $blue; + } + } + + svg { + filter: button--svg-shadow($blue--hard); + } + } + + &--secondary { + background: $green; + border-color: $green--hard; + box-shadow: form--element--box-shadow($green); + text-shadow: button--text-shadow($green--hard); + + &:hover { + background: $green--light; + } + + &:active, + &:focus { + box-shadow: form--element--box-shadow--active($green); + } + + &:active { + background: $green--hard; + border-color: darken($green--hard, 5%); + } + + .inverse & { + border-color: $green; + + &:focus, + &:hover { + background: $green--light; + border-color: $green--light; + } + + &:active, + &:focus { + box-shadow: form--element--inverse--box-shadow--active($green--light); + } + + &:active { + background: $green; + border-color: $green; + } + } + + svg { + filter: button--svg-shadow($green--hard); + } + } + + &--tertiary { + background: $grey; + border-color: $grey--harder; + box-shadow: form--element--box-shadow($grey); + text-shadow: button--text-shadow($grey--hard); + + &:hover { + background: $grey--light; + } + + &:active, + &:focus { + box-shadow: form--element--box-shadow--active($grey); + } + + &:active { + background: $grey--hard; + border-color: darken($grey--hard, 5%); + } + + .inverse & { + border-color: $grey; + + &:focus, + &:hover { + background: $grey--light; + border-color: $grey--light; + } + + &:active, + &:focus { + box-shadow: form--element--inverse--box-shadow--active($grey--light); + } + + &:active { + background: $grey; + border-color: $grey; + } + } + + svg { + filter: button--svg-shadow($grey--hard); + } + } + + &--quaternary { + background: $white; + border-color: $grey--lighter; + box-shadow: form--element--box-shadow($grey--lighter); + color: $grey; + + &:hover { + border-color: $medium-grey; + color: $grey--hard; + } + + &:active, + &:focus { + border-color: $blue; + box-shadow: form--element--box-shadow--active($blue); + color: desaturate($blue--darker, 25%); + } + + &:active { + background-color: $blue--soft; + } + + .inverse & { + background: $input--inverse--background; + border-color: $input--inverse--border; + box-shadow: $form--element--inverse--box-shadow; + color: $input--inverse--foreground; + + &:focus, + &:hover { + border-color: $input--inverse--border--hover; + color: $input--inverse--foreground--hover; + } + + &:active, + &:focus { + border-color: $blue; + box-shadow: form--element--box-shadow--active($blue); + color: $input--inverse--foreground--active; + } + + &:active { + background-color: rgba($blue--soft, 0.15); + } + } + + svg { + filter: button--svg-shadow($grey--lighter); + } + } + + // TODO: Investiate this darker border. + // &--primary, + // &--secondary, + // &--tertiary { + + // .inverse & { + // // border: 1px solid transparent; + // // box-shadow: + // // 0 0 0 1px rgba($input--inverse--border, 0.5), + // // 0 0 0 2px rgba($input--inverse--border, 0.2); + + // &:active, + // &:focus { + // box-shadow: + // 0 0 0 1px rgba($input--inverse--border, 0.2), + // 0 0 0 2px rgba($input--inverse--border, 0.7); + // } + // } + // } + + &--is-disabled { + box-shadow: form--element--box-shadow($blue); + color: $grey--light; + text-shadow: none; + + &, + &:hover, + &:focus, + &:active { + background: $light-grey; + border-color: $light-grey--hard; + box-shadow: none; + cursor: not-allowed; + } + } + + &__content { + align-items: center; + display: flex; + opacity: 1; + overflow: hidden; + text-overflow: ellipsis; + transition: opacity $speed--x-fast; + white-space: nowrap; + } + + .icon { + fill: currentColor; + + &--clipboard { + width: 15px; + } + + &--loading { + left: 50%; + opacity: 0; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + transition: opacity $speed--fast; + } + } + + &--is-loading { + .button { + &__content { + opacity: 0; + } + } + + .icon { + &--loading { + opacity: 1; + } + } + } +} diff --git a/client/src/sass/ui/components/container.scss b/client/src/sass/ui/components/container.scss new file mode 100644 index 000000000..c17fd66fe --- /dev/null +++ b/client/src/sass/ui/components/container.scss @@ -0,0 +1,3 @@ +.container { + max-width: 900px; +} diff --git a/client/src/sass/ui/components/context-menu.scss b/client/src/sass/ui/components/context-menu.scss new file mode 100644 index 000000000..fd5495b75 --- /dev/null +++ b/client/src/sass/ui/components/context-menu.scss @@ -0,0 +1,86 @@ +.context-menu { + font-size: $font-size--small; + height: 100vh; + left: 0; + width: 100vw; + position: absolute; + top: 0; + + &--enter { + .context-menu { + &__items { + opacity: 0; + transform: translateY(2rem * -1); + + &--is-up { + transform: translateY(2rem); + } + } + } + + &--active { + .context-menu { + &__items { + opacity: 1; + transform: translateY(0); + } + } + } + } + + &--exit { + .context-menu { + &__items { + opacity: 1; + transform: translateY(0); + } + } + + &--active { + .context-menu { + &__items { + opacity: 0; + transform: translateY(20px * -1); + + &--is-up { + transform: translateY(20px); + } + } + } + } + } + + &__items { + background: $white; + border-radius: $form--element--border-radius; + box-shadow: form--element--box-shadow($darkest-grey, 0.05, 0.1), 0 0 0 1px rgba($darkest-grey, 0.1); + max-width: 200px; + overflow-x: hidden; + overflow-y: auto; + position: fixed; + transform-origin: 50% -5px; + transition: opacity $speed--xx-fast, transform $speed--xx-fast; + z-index: 101; + + &, + &__padding-surrogate { + padding: $spacing--small 0; + } + + &--is-up { + transform-origin: 50% calc(100% + 5px); + } + + &--match-trigger-width { + max-width: none; + } + + &--no-padding { + padding: 0; + } + + &--no-scrolling { + overflow: hidden; + } + } +} diff --git a/client/src/sass/ui/components/element-addon.scss b/client/src/sass/ui/components/element-addon.scss new file mode 100644 index 000000000..bc3bd899b --- /dev/null +++ b/client/src/sass/ui/components/element-addon.scss @@ -0,0 +1,102 @@ +.form { + &__element { + &--has-addon { + &--placed-before { + padding-left: $button--has-addon--padding--left; + + &.form__element--has-addon--count-2 { + padding-left: $button--has-two-addons--padding--left; + } + } + + &--placed-after { + padding-right: $button--has-addon--padding--right; + + &.form__element--has-addon--count-2 { + padding-right: $button--has-two-addons--padding--right; + } + } + } + + &__addon { + border-width: 0; + border-style: solid; + border-color: $grey--soft; + bottom: 0; + pointer-events: none; + position: absolute; + transition: all $speed--x-fast; + top: 0; + + .inverse & { + border-color: $darkest-grey--darker; + } + + .form__element:focus ~ &, + .form__element:active ~ & { + border-color: rgba($blue, 0.15); + transition: all $speed--xx-fast; + + .icon { + fill: $blue; + transition: all $speed--xx-fast; + + &--stroke { + fill: none; + stroke: $blue; + } + } + } + + .icon { + @extend .icon--small; + fill: $grey; + position: absolute; + left: 50%; + top: 50%; + transform-origin: 50% 50%; + transform: translate(-50%, -50%); + transition: all $speed--x-fast; + + &--stroke { + fill: none; + stroke: $grey; + } + } + + &--placed-before { + border-right-width: 1px; + left: 0; + + &.form__element__addon--index-2 { + left: $button--addon--width; + } + } + + &--placed-after { + border-left-width: 1px; + right: 0; + + &.form__element__addon--index-2 { + right: $button--addon--width; + } + } + + &--is-icon { + width: $button--addon--width; + } + + &--is-interactive { + cursor: pointer; + pointer-events: auto; + + &:hover { + .icon { + fill: $blue; + transition: all $speed--xx-fast; + } + } + } + } + } +} diff --git a/client/src/sass/ui/components/error.scss b/client/src/sass/ui/components/error.scss new file mode 100644 index 000000000..d8e890fae --- /dev/null +++ b/client/src/sass/ui/components/error.scss @@ -0,0 +1,17 @@ +.error { + background: $red; + border-radius: $border-radius--small; + color: #fff; + padding: $form--element--padding--x * 2/3 $form--element--padding--x; + transition: opacity $speed--x-fast; + + &--is-loading { + opacity: 0.25; + } + + .inverse & { + color: #fff; + border: none; + background: $red; + } +} diff --git a/client/src/sass/ui/components/form.scss b/client/src/sass/ui/components/form.scss new file mode 100644 index 000000000..761ec76fa --- /dev/null +++ b/client/src/sass/ui/components/form.scss @@ -0,0 +1,160 @@ +.input, +.button { + appearance: none; + border-radius: $form--element--border-radius; + font-size: $font-size--small; + height: $form--element--height; + outline: none; + padding: $form--element--padding--y $form--element--padding--x; + transition: all $speed--x-fast; + + &:focus, + &:active { + transition: all $speed--xx-fast; + } +} + +.form { + &__row { + display: flex; + margin-bottom: $form__row--margin--vertical; + width: 100%; + + &:last-child, + &--no-margin { + margin-bottom: 0; + } + + &--group { + flex-direction: column; + } + + &--align { + &--start { + align-items: flex-start; + } + + &--center { + align-items: center; + } + + &--end { + align-items: flex-end; + } + } + + &--justify { + &--start { + justify-content: flex-start; + } + + &--center { + justify-content: center; + } + + &--end { + justify-content: flex-end; + } + } + + &__item { + flex: 0 0 auto; + margin: 0 $spacing--x-small; + min-width: 1px; + position: relative; + width: auto; + + &:first-child, + &.is-first { + margin-left: 0; + } + + &:last-child, + &.is-last { + margin-right: 0; + } + + &--grow { + flex-grow: 1; + } + + &--shrink { + flex-shrink: 1; + } + + &--one-eighth { + width: 12.5%; + } + + &--one-quarter { + width: 25%; + } + + &--three-eighths { + width: 37.5%; + } + + &--one-half { + width: 50%; + } + + &--five-eighths { + width: 62.5%; + } + + &--three-quarters { + width: 75%; + } + + &--seven-eighths { + width: 87.5%; + } + + .button, + .checkbox, + .form__element__wrapper, + .input, + .error, + .radio { + width: 100%; + } + + .error { + background: $red--alpha--light; + border: 1px solid $red--alpha--medium; + border-radius: $border-radius--small; + color: $red; + padding: $form--element--padding--x * 2/3 $form--element--padding--x; + transition: opacity $speed--x-fast; + + &--is-loading { + opacity: 0.4; + } + } + } + } + + &__element { + &__label { + display: block; + font-size: $font-size--x-small; + line-height: 1rem; + margin-bottom: $spacing--xx-small; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &__wrapper { + position: relative; + } + + &--label-offset { + margin-top: calc(#{$spacing--xx-small} + 1rem); + } + + &--match-textbox-height { + height: $form--element--height; + } + } +} diff --git a/client/src/sass/ui/components/icon.scss b/client/src/sass/ui/components/icon.scss new file mode 100644 index 000000000..5674d203b --- /dev/null +++ b/client/src/sass/ui/components/icon.scss @@ -0,0 +1,41 @@ +@keyframes rotateAroundMidpoint { + 0% { + transform: rotate(0); + } + + 100% { + transform: rotate(360deg); + } +} + +.icon { + position: relative; + + &--small { + height: 18px; + width: 18px; + } + + &--large { + height: 24px; + width: 24px; + } + + &__element { + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + } + + &--loading { + &--ring { + stroke: currentColor; + } + } + + &__ring-slice { + animation: 1s linear infinite forwards rotateAroundMidpoint; + } +} diff --git a/client/src/sass/ui/components/input.scss b/client/src/sass/ui/components/input.scss new file mode 100644 index 000000000..b56f685bb --- /dev/null +++ b/client/src/sass/ui/components/input.scss @@ -0,0 +1,225 @@ +.input { + background: $input--background; + color: $input--foreground; + border: $form--element--border-width solid $input--border; + box-shadow: $form--element--box-shadow; + + &::placeholder { + color: $input--foreground--placeholder; + transition: color $speed--xx-fast; + } + + &:hover { + border-color: $input--border--hover; + color: $input--foreground--hover; + + &::placeholder { + color: $input--foreground--placeholder--hover; + } + } + + &:active, + &:focus { + background-color: $blue--soft; + color: desaturate($blue--darker, 25%); + border-color: $blue; + box-shadow: form--element--box-shadow--active($blue); + + &::placeholder { + color: rgba($blue--light, 0.5); + } + } + + .inverse & { + background: $input--inverse--background; + border: $form--element--border-width solid $input--inverse--border; + box-shadow: $form--element--inverse--box-shadow; + color: $input--inverse--foreground; + + &::placeholder { + color: $input--inverse--foreground--placeholder; + } + + &:hover { + border-color: $input--inverse--border--hover; + color: $input--inverse--foreground--hover; + + &::placeholder { + color: $input--inverse--foreground--placeholder--hover; + } + } + + &:active, + &:focus { + background-color: rgba($blue--soft, 0.15); + color: $input--inverse--foreground--active; + border-color: $blue; + box-shadow: form--element--box-shadow--active($blue); + + &::placeholder { + color: $input--inverse--foreground--placeholder--active; + } + } + } + + &--hidden { + left: 0; + opacity: 0; + position: absolute; + top: 0; + z-index: -1; + } +} + +.toggle-input { + align-items: center; + cursor: pointer; + display: flex; + outline: none; + padding: $form--element--padding--y 0 $form--element--padding--y $toggle-input--width + $spacing--x-small; + position: relative; + transition: all $speed--x-fast; + user-select: none; + + &:hover { + transition: all $speed--xx-fast; + + .toggle-input { + &__indicator { + border-color: $input--border--hover; + + .inverse & { + border-color: $input--inverse--border--hover; + } + + &, + &__icon { + transition: all $speed--xx-fast; + } + + &__icon { + &, + .icon { + display: block; + } + } + } + } + } + + &:focus, + &:active { + color: $blue; + + .toggle-input { + &__indicator { + border-color: $blue; + box-shadow: form--element--box-shadow--active($blue); + + .inverse & { + border-color: $blue; + box-shadow: form--element--box-shadow--active($blue); + } + + &, + &__icon { + transition: all $speed--xx-fast; + + .icon { + fill: $blue; + } + } + } + } + } + + &:active, + &--is-active { + .toggle-input { + &__indicator { + background-color: rgba($blue, 0.1); + color: rgba($blue, 0.8); + + .inverse & { + background-color: rgba($blue, 0.1); + color: rgba($blue, 0.8); + } + + &__icon { + opacity: 0.25; + } + } + } + } + + &__element { + position: absolute; + visibility: hidden; + + &:checked { + & + .toggle-input { + &__indicator { + .toggle-input { + &__indicator { + &__icon { + opacity: 1; + } + } + } + } + } + } + } + + &__indicator { + background: $input--background; + border: $form--element--border-width solid $input--border; + box-shadow: $form--element--box-shadow; + display: inline-block; + height: $toggle-input--height; + left: 0; + position: absolute; + top: 50%; + transform: translateY(-50%); + transition: all $speed--x-fast; + width: $toggle-input--width; + + &__icon { + opacity: 0; + transition: all $speed--x-fast; + + &, + .icon { + height: 100%; + width: 100%; + } + + .icon { + fill: $grey; + transition: all $speed--x-fast; + } + } + + .inverse & { + background: $input--inverse--background; + border: $form--element--border-width solid $input--inverse--border; + box-shadow: $form--element--inverse--box-shadow; + } + } + + &.checkbox { + .toggle-input { + &__indicator { + border-radius: $form--element--border-radius; + } + } + } + + &.radio { + .toggle-input { + &__indicator { + border-radius: 100%; + } + } + } +} diff --git a/client/src/sass/ui/components/overlay.scss b/client/src/sass/ui/components/overlay.scss new file mode 100644 index 000000000..a70a55455 --- /dev/null +++ b/client/src/sass/ui/components/overlay.scss @@ -0,0 +1,17 @@ +.overlay { + background: rgba($darker-grey, 0.95); + bottom: 0; + left: 0; + position: fixed; + right: 0; + top: 0; + z-index: 100; + + &--transparent { + background: transparent; + } + + &--no-interaction { + pointer-events: none; + } +} diff --git a/client/src/sass/ui/components/panel.scss b/client/src/sass/ui/components/panel.scss new file mode 100644 index 000000000..1e215bd5f --- /dev/null +++ b/client/src/sass/ui/components/panel.scss @@ -0,0 +1,107 @@ +$panel--spacing--horizontal--medium: $spacing--x-large; +$panel--spacing--vertical--medium: $spacing--medium; + +$panel--spacing--horizontal--large: $spacing--xxx-large; +$panel--spacing--vertical--large: $spacing--xx-large; + +.panel { + border: 1px solid $grey--lighter; + border-radius: $border-radius--medium; + box-shadow: panel--box-shadow($grey--lighter); + overflow: hidden; + + &--medium { + .panel { + &__content { + padding: $panel--spacing--vertical--medium $panel--spacing--horizontal--medium; + } + + &__header { + padding: $panel--spacing--vertical--medium $panel--spacing--horizontal--medium 0 + $panel--spacing--horizontal--medium; + + &--has-border { + padding-bottom: $panel--spacing--vertical--medium; + } + } + + &__footer { + padding: 0 $panel--spacing--horizontal--medium $panel--spacing--vertical--medium + $panel--spacing--horizontal--medium; + + &--has-border { + padding-top: $panel--spacing--vertical--medium; + } + } + } + } + + &--large { + .panel { + &__content { + padding: $panel--spacing--vertical--large $panel--spacing--horizontal--large; + } + + &__header { + padding: $panel--spacing--vertical--large $panel--spacing--horizontal--large 0 + $panel--spacing--horizontal--large; + + &--has-border { + padding-bottom: $panel--spacing--vertical--large; + } + } + + &__footer { + padding: 0 $panel--spacing--horizontal--large $panel--spacing--vertical--large + $panel--spacing--horizontal--large; + + &--has-border { + padding-top: $panel--spacing--vertical--large; + } + } + } + } + + &__header { + &--has-border { + border-bottom: 1px solid $grey--soft; + } + + h1, + h2, + h3, + h4, + h5, + h6, + .h1, + .h2, + .h3, + .h4, + .h5, + .h6 { + margin: 0; + } + } + + &__content { + &--has-border { + &--top { + border-top: 1px solid $grey--soft; + } + + &--bottom { + border-bottom: 1px solid $grey--soft; + } + } + } + + &__footer { + &--has-border { + border-top: 1px solid $grey--soft; + } + } + + &--light { + background: #fff; + } +} diff --git a/client/src/sass/ui/components/portal.scss b/client/src/sass/ui/components/portal.scss new file mode 100644 index 000000000..af1afc745 --- /dev/null +++ b/client/src/sass/ui/components/portal.scss @@ -0,0 +1,5 @@ +.portal { + overflow: hidden; + position: relative; + z-index: 1000; +} diff --git a/client/src/sass/ui/components/section.scss b/client/src/sass/ui/components/section.scss new file mode 100644 index 000000000..0b896a2f0 --- /dev/null +++ b/client/src/sass/ui/components/section.scss @@ -0,0 +1,16 @@ +.section { + margin-bottom: $spacing--xxx-large; + + &__heading { + margin-bottom: $spacing; + margin-top: 0; + } + + &.inverse { + background: $another-grey; + } + + &.padded { + padding: $spacing--large $spacing--x-large; + } +} diff --git a/client/src/sass/ui/components/select.scss b/client/src/sass/ui/components/select.scss new file mode 100644 index 000000000..c47dab762 --- /dev/null +++ b/client/src/sass/ui/components/select.scss @@ -0,0 +1,87 @@ +.select { + position: relative; + + &__button { + position: relative; + text-align: left; + + &:hover { + .select { + &__indicator { + .icon { + fill: $grey--hard; + } + } + } + } + + &:focus, + &:active { + .select { + &__indicator { + .icon { + fill: $blue; + transition: all $speed--xx-fast; + } + } + } + } + + .icon { + display: none; + } + + .select { + &__item { + overflow: hidden; + text-overflow: ellipsis; + } + } + } + + &__item { + cursor: pointer; + padding: $spacing--x-small $spacing--small; + position: relative; + transition: all $speed--xx-fast; + z-index: 1; + + .context-menu__items & { + &:hover { + background: $light-grey--lighter; + } + } + + .icon { + fill: $grey; + height: 14px; + right: $spacing--small; + opacity: 0; + position: absolute; + top: 50%; + transform: translateY(-60%); + transition: all $speed--xx-fast; + width: 14px; + } + + &--is-selected { + color: $blue; + font-weight: 500; + + .icon { + fill: $blue; + opacity: 1; + } + } + } + + &--is-open { + .select { + &__indicator { + .icon { + transform: translate(-50%, -50%) rotate(180deg); + } + } + } + } +} diff --git a/client/src/sass/ui/config/border-radius.scss b/client/src/sass/ui/config/border-radius.scss new file mode 100644 index 000000000..bc9ecf876 --- /dev/null +++ b/client/src/sass/ui/config/border-radius.scss @@ -0,0 +1,3 @@ +$border-radius--large: $spacing--small; +$border-radius--medium: $spacing--xx-small + 1; +$border-radius--small: $spacing--xx-small; diff --git a/client/src/sass/ui/config/font-size.scss b/client/src/sass/ui/config/font-size.scss new file mode 100644 index 000000000..04f126e32 --- /dev/null +++ b/client/src/sass/ui/config/font-size.scss @@ -0,0 +1,9 @@ +$base-font-size: 16px; + +$font-size--xx-large: (1 + 3/8) + 0rem; +$font-size--x-large: (1 + 1/4) + 0rem; +$font-size--large: (1 + 1/8) + 0rem; +$font-size--medium: 1rem; +$font-size--small: (7/8) + 0rem; +$font-size--x-small: (3/4) + 0rem; +$font-size--xx-small: (5/8) + 0rem; diff --git a/client/src/sass/ui/config/spacing.scss b/client/src/sass/ui/config/spacing.scss new file mode 100644 index 000000000..5365e607f --- /dev/null +++ b/client/src/sass/ui/config/spacing.scss @@ -0,0 +1,20 @@ +$spacing: 12px; + +$spacing--xx-small: $spacing * 1/4; +$spacing--x-small: $spacing * 2/4; +$spacing--small: $spacing * 3/4; +$spacing--medium: $spacing * 5/4; +$spacing--large: $spacing * 6/4; +$spacing--x-large: $spacing * 7/4; +$spacing--xx-large: $spacing * 8/4; +$spacing--xxx-large: $spacing * 12/4; + +// Specifics +$app-panel--padding--x: $spacing--large; +$app-panel--padding--y: $spacing--xx-large; + +$app--heading--padding--x: $app-panel--padding--x; +$app--heading--padding--y: $spacing--x-small; + +$app--sidebar--item--padding--x: $app-panel--padding--x; +$app--sidebar--item--padding--y: $spacing--small; diff --git a/client/src/sass/ui/config/speed.scss b/client/src/sass/ui/config/speed.scss new file mode 100644 index 000000000..5dc465521 --- /dev/null +++ b/client/src/sass/ui/config/speed.scss @@ -0,0 +1,9 @@ +$base-speed: 1s; + +$speed--xx-slow: 7/8 * $base-speed; +$speed--x-slow: 3/4 * $base-speed; +$speed--slow: 5/8 * $base-speed; +$speed--medium: 1/2 * $base-speed; +$speed--fast: 3/8 * $base-speed; +$speed--x-fast: 1/4 * $base-speed; +$speed--xx-fast: 1/8 * $base-speed; diff --git a/client/src/sass/ui/config/variables.scss b/client/src/sass/ui/config/variables.scss new file mode 100644 index 000000000..b9a3bd8d8 --- /dev/null +++ b/client/src/sass/ui/config/variables.scss @@ -0,0 +1,52 @@ +// Forms +$form--element--border-radius: $border-radius--medium; +$form--element--border-width: 1px; +$form--element--height: 34px; +$form--element--padding--x: $spacing--small + $spacing--xx-small; +$form--element--padding--y: 0; +$form__row--margin--vertical: $spacing--medium; + +// Checkboxes & Radio buttons +$toggle-input--height: $spacing--large; +$toggle-input--width: $spacing--large; + +// Select +$select--indicator--width: $form--element--padding--x * 2 + $spacing--small; +$select--button--padding-right: $select--indicator--width + $spacing--x-small; + +// Input Elements +$input--background: $white; +$input--border: $grey--lighter; +$input--border--hover: $medium-grey; +$form--element--box-shadow: form--element--box-shadow($grey--lighter); +$input--foreground: $grey; +$input--foreground--hover: $grey--hard; +$input--foreground--placeholder: rgba($medium-grey, 0.5); +$input--foreground--placeholder--hover: rgba($grey--light, 0.6); + +// Inverse input Elements +$input--inverse--background: $darkest-grey--hard; +$input--inverse--border: $darkest-grey--darker; +$input--inverse--border--hover: darken($darkest-grey--darker, 5%); +$form--element--inverse--box-shadow: form--element--box-shadow($darkest-grey--hard); +$input--inverse--foreground: $grey; +$input--inverse--foreground--hover: $grey--lighter; +$input--inverse--foreground--active: $blue--lighter; +$input--inverse--foreground--placeholder--active: rgba(lighten($blue, 15%), 0.5); +$input--inverse--foreground--placeholder: rgba($medium-grey, 0.25); +$input--inverse--foreground--placeholder--hover: rgba($medium-grey, 0.4); + +$input--addon--width: $form--element--padding--x * 2 + $spacing--small + $form--element--border-width; +$input--addon--padding--right: $input--addon--width + $form--element--padding--x / 2; +$input--addon--padding--left: $input--addon--width + $form--element--padding--x / 2; +$input--two-addons--padding--right: $input--addon--width * 2 + $form--element--padding--x / 2; +$input--two-addons--padding--left: $input--addon--width * 2 + $form--element--padding--x / 2; + +// Button +$button--addon--width: $input--addon--width; +$button--has-addon--padding--right: $input--addon--padding--right; +$button--has-addon--padding--left: $input--addon--padding--left; +$button--has-two-addons--padding--right: $input--two-addons--padding--right; +$button--has-two-addons--padding--left: $input--two-addons--padding--left; + +$font-family--monospace: monaco, Consolas, 'Lucida Console', monospace; diff --git a/client/src/sass/ui/index.scss b/client/src/sass/ui/index.scss new file mode 100644 index 000000000..ea86eb6f7 --- /dev/null +++ b/client/src/sass/ui/index.scss @@ -0,0 +1,21 @@ +@import './util/box-shadows'; + +@import './config/spacing'; +@import './config/border-radius'; +@import './config/font-size'; +@import './config/speed'; +@import './config/variables'; + +@import './components/button'; +@import './components/context-menu'; +@import './components/container'; +@import './components/error'; +@import './components/form'; +@import './components/element-addon'; +@import './components/icon'; +@import './components/input'; +@import './components/overlay'; +@import './components/panel'; +@import './components/portal'; +@import './components/section'; +@import './components/select'; diff --git a/client/src/sass/ui/util/box-shadows.scss b/client/src/sass/ui/util/box-shadows.scss new file mode 100644 index 000000000..87de38ad9 --- /dev/null +++ b/client/src/sass/ui/util/box-shadows.scss @@ -0,0 +1,23 @@ +@function form--element--box-shadow($color, $smallShadowOpacity: 0.15, $bigShadowOpacity: 0.2) { + @return 0 1px 1px rgba($color, $smallShadowOpacity), 0 1px 3px 1px rgba($color, $bigShadowOpacity); +} + +@function form--element--box-shadow--active($color) { + @return form--element--box-shadow($color), 0 0 0 1px rgba($color, 1); +} + +@function form--element--inverse--box-shadow--active($color) { + @return 0 0 0 2px rgba($color, 0.4); +} + +@function form--element--box-shadow--active--slim($color) { + @return form--element--box-shadow($color), 0 0 0 2px rgba($color, 0.2); +} + +@function form--element--box-shadow--active--smaller($color) { + @return form--element--box-shadow($color), 0 0 0 2px rgba($color, 0.2); +} + +@function panel--box-shadow($color) { + @return form--element--box-shadow($color); +} diff --git a/client/src/sass/views/_feeds.scss b/client/src/sass/views/_feeds.scss index 31cd4ad7e..aa74f4452 100644 --- a/client/src/sass/views/_feeds.scss +++ b/client/src/sass/views/_feeds.scss @@ -1,8 +1,7 @@ .feed-list { - &__feed-label { display: block; overflow: hidden; text-overflow: ellipsis; } -} \ No newline at end of file +} diff --git a/client/src/sass/views/_login.scss b/client/src/sass/views/_login.scss index 2dc50f009..3d2ccd424 100644 --- a/client/src/sass/views/_login.scss +++ b/client/src/sass/views/_login.scss @@ -9,9 +9,7 @@ $authentication--header--foreground: $white; $authentication--label--foreground: #3a5567; .application { - &__view { - &--auth-form { background: $light-blue; } @@ -19,13 +17,11 @@ $authentication--label--foreground: #3a5567; } .form { - &--authentication { max-width: 350px; width: 100%; .form { - &__wrapper { background: $white; border: 1px solid $authentication--form--border; @@ -34,7 +30,8 @@ $authentication--label--foreground: #3a5567; &__header { background: $blue; - margin: $authentication--form--padding * -1 $authentication--form--padding * -1 0 $authentication--form--padding * -1; + margin: $authentication--form--padding * -1 $authentication--form--padding * -1 0 $authentication--form--padding * + -1; padding: $authentication--form--padding * 0.75 $authentication--form--padding; h1 { @@ -50,7 +47,6 @@ $authentication--label--foreground: #3a5567; } &--header { - } &__label { @@ -58,7 +54,6 @@ $authentication--label--foreground: #3a5567; } &__row { - &--error { color: $red; font-size: 0.75em; diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..bfdc9877d --- /dev/null +++ b/codecov.yml @@ -0,0 +1,8 @@ +coverage: + status: + project: + default: + informational: true + patch: + default: + informational: true diff --git a/config.cli.js b/config.cli.js new file mode 100644 index 000000000..92077ffb8 --- /dev/null +++ b/config.cli.js @@ -0,0 +1,297 @@ +const {spawn} = require('child_process'); +const crypto = require('crypto'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const {argv} = require('yargs') + .env('FLOOD_OPTION_') + .option('baseuri', { + default: '/', + describe: "This URI will prefix all of Flood's HTTP requests", + type: 'string', + }) + .option('rundir', { + alias: 'd', + default: path.join(os.homedir(), '.local/share/flood'), + describe: "Where to store Flood's runtime files (eg. database)", + type: 'string', + }) + .option('host', { + alias: 'h', + default: '127.0.0.1', + describe: 'The host that Flood should listen for web connections on', + type: 'string', + }) + .option('port', { + alias: 'p', + default: 3000, + describe: 'The port that Flood should listen for web connections on', + type: 'number', + }) + .option('secret', { + alias: 's', + hidden: true, + describe: 'A unique secret, a random one will be generated if not provided', + type: 'string', + }) + .option('auth', { + describe: 'Access control and user management method', + choices: ['default', 'none'], + }) + .option('noauth', { + alias: 'n', + hidden: true, + default: false, + describe: "Disable Flood's builtin access control system, deprecated, use auth=none instead", + type: 'boolean', + }) + .option('rthost', { + describe: "Host of rTorrent's SCGI interface", + type: 'string', + }) + .option('rtport', { + describe: "Port of rTorrent's SCGI interface", + type: 'number', + }) + .option('rtsocket', { + conflicts: ['rthost', 'rtport'], + describe: "Path to rTorrent's SCGI unix socket", + type: 'string', + }) + .option('qburl', { + describe: 'URL to qBittorrent Web API', + type: 'string', + }) + .option('qbuser', { + describe: 'Username of qBittorrent Web API', + type: 'string', + }) + .option('qbpass', { + describe: 'Password of qBittorrent Web API', + type: 'string', + }) + .option('trurl', { + describe: 'URL to Transmission RPC interface', + type: 'string', + }) + .option('truser', { + describe: 'Username of Transmission RPC interface', + type: 'string', + }) + .option('trpass', { + describe: 'Password of Transmission RPC interface', + type: 'string', + }) + .group(['rthost', 'rtport', 'rtsocket', 'qburl', 'qbuser', 'qbpass', 'trurl', 'truser', 'trpass'], 'When auth=none:') + .option('ssl', { + default: false, + describe: 'Enable SSL, key.pem and fullchain.pem needed in runtime directory', + type: 'boolean', + }) + .option('sslkey', { + describe: 'Depends on ssl: Absolute path to private key for SSL', + implies: 'ssl', + hidden: true, + type: 'string', + }) + .option('sslcert', { + describe: 'Depends on ssl: Absolute path to fullchain cert for SSL', + implies: 'ssl', + hidden: true, + type: 'string', + }) + .option('allowedpath', { + describe: 'Allowed path for file operations, can be called multiple times', + type: 'string', + }) + .option('assets', { + default: true, + describe: 'ADVANCED: Serve static assets', + hidden: true, + type: 'boolean', + }) + .option('dbclean', { + default: 1000 * 60 * 60, + describe: 'ADVANCED: Interval between database purge', + hidden: true, + type: 'number', + }) + .option('maxhistorystates', { + default: 30, + describe: 'ADVANCED: Number of records of torrent download and upload speeds', + hidden: true, + type: 'number', + }) + .option('clientpoll', { + default: 1000 * 2, + describe: 'ADVANCED: How often (in ms) Flood will request the torrent list', + hidden: true, + type: 'number', + }) + .option('clientpollidle', { + default: 1000 * 60 * 15, + describe: 'ADVANCED: How often (in ms) Flood will request the torrent list when no user is present', + hidden: true, + type: 'number', + }) + .option('rtorrent', { + default: false, + describe: 'ADVANCED: rTorrent daemon managed by Flood', + hidden: true, + type: 'boolean', + }) + .option('rtconfig', { + describe: 'ADVANCED: rtorrent.rc for managed rTorrent daemon', + implies: 'rtorrent', + hidden: true, + type: 'string', + }) + .option('proxy', { + default: 'http://127.0.0.1:3000', + describe: 'DEV ONLY: See the "Local Development" section of README.md', + hidden: true, + type: 'string', + }) + .option('test', { + default: false, + describe: 'DEV ONLY: Test setup', + hidden: true, + type: 'boolean', + }) + .version(require('./package.json').version) + .alias('v', 'version') + .help(); + +process.on('SIGINT', () => { + process.exit(); +}); + +try { + fs.mkdirSync(path.join(argv.rundir, 'db'), {recursive: true}); + fs.mkdirSync(path.join(argv.rundir, 'temp'), {recursive: true}); +} catch (error) { + console.error('Failed to access runtime directory'); + process.exit(1); +} + +if (argv.rtorrent) { + const args = []; + let opts = 'system.daemon.set=true'; + + if (typeof argv.rtconfig === 'string' && argv.rtconfig.length > 0) { + args.push('-n'); + opts += `,import=${argv.rtconfig}`; + } + + const rTorrentProcess = spawn('rtorrent', args.concat(['-o', opts]), {stdio: 'inherit'}); + + fs.writeFileSync(path.join(argv.rundir, 'rtorrent.pid'), `${rTorrentProcess.pid}`); + + if (!argv.test) { + rTorrentProcess.on('close', () => { + process.exit(1); + }); + rTorrentProcess.on('error', () => { + process.exit(1); + }); + } + + process.on('exit', () => { + console.log('Killing rTorrent daemon...'); + rTorrentProcess.kill('SIGTERM'); + }); +} + +const DEFAULT_SECRET_PATH = path.join(argv.rundir, 'flood.secret'); +let secret; + +if (!argv.secret) { + try { + if (fs.existsSync(DEFAULT_SECRET_PATH)) { + secret = fs.readFileSync(DEFAULT_SECRET_PATH, {encoding: 'utf8'}); + } else { + const buf = Buffer.alloc(36); + crypto.randomFillSync(buf); + secret = buf.toString('hex'); + fs.writeFileSync(DEFAULT_SECRET_PATH, secret, {mode: 0o600}); + } + } catch (error) { + console.error('Failed to read or generate secret'); + process.exit(1); + } +} else { + ({secret} = argv); +} + +let connectionSettings; +if (argv.rtsocket != null || argv.rthost != null) { + if (argv.rtsocket != null) { + connectionSettings = { + client: 'rTorrent', + type: 'socket', + version: 1, + socket: argv.rtsocket, + }; + } else { + connectionSettings = { + client: 'rTorrent', + type: 'tcp', + version: 1, + host: argv.rthost, + port: argv.rtport, + }; + } +} else if (argv.qburl != null) { + connectionSettings = { + client: 'qBittorrent', + type: 'web', + version: 1, + url: argv.qburl, + username: argv.qbuser, + password: argv.qbpass, + }; +} else if (argv.trurl != null) { + connectionSettings = { + client: 'Transmission', + type: 'rpc', + version: 1, + url: argv.trurl, + username: argv.truser, + password: argv.trpass, + }; +} + +let authMethod = 'default'; +if (argv.noauth || argv.auth === 'none') { + authMethod = 'none'; +} + +let allowedPaths = []; +if (typeof argv.allowedpath === 'string') { + allowedPaths = allowedPaths.concat(argv.allowedpath.split(',')); +} else if (Array.isArray(argv.allowedpath)) { + allowedPaths = allowedPaths.concat(argv.allowedpath); +} + +const CONFIG = { + baseURI: argv.baseuri, + dbCleanInterval: argv.dbclean, + dbPath: path.resolve(path.join(argv.rundir, 'db')), + tempPath: path.resolve(path.join(argv.rundir, 'temp')), + authMethod, + configUser: connectionSettings, + floodServerHost: argv.host, + floodServerPort: argv.port, + floodServerProxy: argv.proxy, + maxHistoryStates: argv.maxhistorystates, + torrentClientPollInterval: argv.clientpoll, + torrentClientPollIntervalIdle: argv.clientpollidle, + secret, + ssl: argv.ssl, + sslKey: argv.sslkey || path.resolve(path.join(argv.rundir, 'key.pem')), + sslCert: argv.sslcert || path.resolve(path.join(argv.rundir, 'fullchain.pem')), + allowedPaths: allowedPaths.length > 0 ? allowedPaths : undefined, + serveAssets: argv.assets, +}; + +module.exports = CONFIG; diff --git a/config.d.ts b/config.d.ts new file mode 100644 index 000000000..101a3bec2 --- /dev/null +++ b/config.d.ts @@ -0,0 +1,7 @@ +// @see shared/schema/Config + +import type {Config} from '@shared/schema/Config'; + +declare const CONFIG: Config; + +export = CONFIG; diff --git a/config.docker.js b/config.docker.js deleted file mode 100644 index 6968621d4..000000000 --- a/config.docker.js +++ /dev/null @@ -1,20 +0,0 @@ -const CONFIG = { - baseURI: process.env.FLOOD_BASE_URI || '/', - dbCleanInterval: 1000 * 60 * 60, - dbPath: '/data/server/db/', - floodServerPort: 3000, - maxHistoryStates: 30, - pollInterval: 1000 * 5, - secret: process.env.FLOOD_SECRET || 'flood', - scgi: { - host: process.env.RTORRENT_SCGI_HOST || 'localhost', - port: process.env.RTORRENT_SCGI_PORT || 5000, - socket: process.env.RTORRENT_SOCK === 'true' || process.env.RTORRENT_SOCK === true, - socketPath: process.env.RTORRENT_SOCK_PATH || '/data/rtorrent.sock', - }, - ssl: process.env.FLOOD_ENABLE_SSL === 'true' || process.env.FLOOD_ENABLE_SSL === true, - sslKey: '/data/flood_ssl.key', - sslCert: '/data/flood_ssl.cert', -}; - -module.exports = CONFIG; diff --git a/config.js b/config.js new file mode 120000 index 000000000..2fd52e8c0 --- /dev/null +++ b/config.js @@ -0,0 +1 @@ +config.cli.js \ No newline at end of file diff --git a/config.template.js b/config.template.js deleted file mode 100644 index 823c8f5c3..000000000 --- a/config.template.js +++ /dev/null @@ -1,53 +0,0 @@ -// This is the configuration file for Flood, a React-based frontend for the -// rtorrent BitTorrent client. -// Copy this file to ./config.js and make changes below. -// config.js must exist before running `npm run build`. - -const CONFIG = { - // This URI will prefix all of Flood's HTTP requests. You _must_ have a web - // server, like nginx, configured to forward these requests to the Flood - // web server. - // For example, if you intend to serve from http://example.com/flood, set this to - // '/flood' and configure your web server to pass _all_ requests from `/flood` to - // the root of Flood's web server. - // Recompiling assets with `npm run build` is needed after each `baseURI` change. - // See https://github.com/Flood-UI/flood/wiki/Using-Flood-behind-a-reverse-proxy - baseURI: '/', - // Flood uses a local nedb database to keep track of users, torrents, - // and activity. The database is regularly purged to remove outdated data. - // This value dictates how old data is, in milliseconds, before being purged. - dbCleanInterval: 1000 * 60 * 60, - // Where to store the local nedb database. - dbPath: './server/db/', - // The host that Flood should listen for web connections on. - // If you want to connect to Flood from hosts other that the one it is running - // on, you should change this value. - // To listen on all interfaces, change to `floodServerHost: '0.0.0.0'`.. - floodServerHost: '127.0.0.1', - // The port that Flood should listen for web connections on. - floodServerPort: 3000, - // Used for development. See the "Local Development" section of README.md - // for detail. - floodServerProxy: 'http://127.0.0.1:3000', - // Flood keeps a history of torrent download and upload speeds. - // This value dictates the number of individual records per period to keep. - maxHistoryStates: 30, - // How often (in milliseconds) Flood will request the torrent list from. - torrentClientPollInterval: 1000 * 2, - // A unique secret for signing messages with JWT (see https://jwt.io). Change - // this to something unique and hard to guess. - secret: 'flood', - // Configuration for SSL, if using SSL with the Flood service directly. - ssl: false, - sslKey: '/absolute/path/to/key/', - sslCert: '/absolute/path/to/certificate/', - // disk space service checks disk space of mounted partitions - diskUsageService: { - // assign desired mounts to include. Refer to "Mounted on" column of `df -P` - // watchMountPoints: [ - // "/mnt/disk" - // ] - } -}; -// Do not remove the below line. -module.exports = CONFIG; diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 000000000..dc98e3a29 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,5 @@ +commit_message: 'i18n: new translations for %language%' +append_commit_message: false +files: + - source: /client/src/javascript/i18n/strings.json + translation: /client/src/javascript/i18n/translations/%osx_locale%.json diff --git a/custom.d.ts b/custom.d.ts deleted file mode 100644 index 091d25e21..000000000 --- a/custom.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.svg' { - const content: any; - export default content; -} diff --git a/cypress.json b/cypress.json new file mode 100644 index 000000000..4ef9c8dd5 --- /dev/null +++ b/cypress.json @@ -0,0 +1,3 @@ +{ + "projectId": "akrovg" +} diff --git a/cypress/integration/form-elements.spec.ts b/cypress/integration/form-elements.spec.ts new file mode 100644 index 000000000..b64663bbb --- /dev/null +++ b/cypress/integration/form-elements.spec.ts @@ -0,0 +1,128 @@ +context('Form elements', () => { + beforeEach(() => { + cy.visit('http://127.0.0.1:4200/overview'); + cy.url().should('include', 'overview'); + cy.get('.application__loading-overlay').should('not.exist'); + + // Add torrents modal + cy.get('.action--add-torrent').should('be.visible'); + cy.get('.action--add-torrent').click(); + cy.get('.modal__header').contains('Add Torrents').should('be.visible'); + }); + + afterEach(() => { + cy.get('.button__content').contains('Cancel').should('be.visible'); + cy.get('.button__content').contains('Cancel').click(); + cy.get('.modal__header').should('not.be.visible'); + }); + + it('Textboxes', () => { + cy.get('input[name="urls-0"]').should('be.visible'); + cy.get('input[name="urls-0"]').parents('.form__row--group').as('textboxesGroup'); + cy.get('input[name="urls-0"]').type('test'); + + // Add textboxes with foo and bar + cy.get('input[name="urls-0"]').siblings().first().click(); + cy.get('input[name="urls-1"]').should('be.visible'); + cy.get('input[name="urls-1"]').type('foo'); + + cy.get('input[name="urls-1"]').siblings().first().click(); + cy.get('input[name="urls-2"]').should('be.visible'); + cy.get('input[name="urls-2"]').type('bar'); + + // Insert test2 + cy.get('input[name="urls-0"]').siblings().first().click(); + cy.get('input[name="urls-3"]').should('be.visible'); + cy.get('input[name="urls-3"]').type('test2'); + + // Assert order: test, test2, foo, bar + cy.get('@textboxesGroup').children().eq(0).find('input').should('have.value', 'test'); + cy.get('@textboxesGroup').children().eq(1).find('input').should('have.value', 'test2'); + cy.get('@textboxesGroup').children().eq(2).find('input').should('have.value', 'foo'); + cy.get('@textboxesGroup').children().eq(3).find('input').should('have.value', 'bar'); + + // Deletes test2 + cy.get('input[name="urls-3"]').siblings().last().click(); + + // Assert order: test, foo, bar + cy.get('@textboxesGroup').children().eq(0).find('input').should('have.value', 'test'); + cy.get('@textboxesGroup').children().eq(1).find('input').should('have.value', 'foo'); + cy.get('@textboxesGroup').children().eq(2).find('input').should('have.value', 'bar'); + + // Deletes bar + cy.get('input[name="urls-2"]').siblings().last().click(); + + // Assert order: test, foo + cy.get('@textboxesGroup').children().eq(0).find('input').should('have.value', 'test'); + cy.get('@textboxesGroup').children().eq(1).find('input').should('have.value', 'foo'); + }); + + it('Tag selector', () => { + // Tag input + cy.get('.input[name="tags"]').should('be.visible'); + cy.get('.input[name="tags"]').type('foo,bar'); + + // Open selector + cy.get('.input[name="tags"]').siblings().click(); + cy.get('.context-menu__item').contains('Untagged').should('be.visible'); + + // Expect foo and bar selected + cy.get('.select__item--is-selected').first().should('contain', 'foo'); + cy.get('.select__item--is-selected').last().should('contain', 'bar'); + + // Unselect foo + cy.get('.select__item--is-selected').first().click(); + cy.get('.input[name="tags"]').should('have.value', 'bar'); + + // Click "Untagged" + cy.get('.context-menu__item').contains('Untagged').click(); + cy.get('.input[name="tags"]').should('have.value', ''); + + // Close + cy.get('.input[name="tags"]').siblings().click(); + cy.get('.context-menu__item').should('not.exist'); + }); + + it('Filesystem browser', () => { + // Filesystem browser + cy.get('.input[name="destination"]').then((destinationElem) => { + const destination = Cypress.$(destinationElem).val(); + + // Open fs browser + cy.get('.form__element__addon .icon--search').parent().click(); + cy.get('.filesystem__directory-list').should('be.visible'); + + // Go into the first directory + cy.get('.filesystem__directory-list__item--selectable').first().click(); + + // Expect destination to change + cy.get('.input[name="destination"]').should('not.have.value', destination); + + // Go back + cy.get('.filesystem__directory-list__item--parent').click(); + + // Expect destination to match stored value + cy.get('.input[name="destination"]').should('have.value', destination); + + // Close + cy.get('.form__element__addon .icon--search').parent().click(); + cy.get('.filesystem__directory-list').should('not.exist'); + }); + }); + + it('Toggle', () => { + cy.get('input[name="start"]').then((startToggleElem) => { + const start = Cypress.$(startToggleElem).attr('checked') != null ? true : false; + + // Click toggle + cy.get('input[name="start"]').siblings().find('.icon--checkmark').click(); + + // Expect change of state + if (start) { + cy.get('input[name="start"]').siblings().find('.icon--checkmark').should('not.be.visible'); + } else { + cy.get('input[name="start"]').siblings().find('.icon--checkmark').should('be.visible'); + } + }); + }); +}); diff --git a/cypress/integration/login.spec.ts b/cypress/integration/login.spec.ts new file mode 100644 index 000000000..da2e3f72e --- /dev/null +++ b/cypress/integration/login.spec.ts @@ -0,0 +1,58 @@ +context('Login', () => { + beforeEach(() => { + cy.server(); + cy.route({ + method: 'GET', + url: 'http://127.0.0.1:4200/api/auth/verify?*', + response: {}, + status: 401, + }).as('verify-request'); + cy.visit('http://127.0.0.1:4200/login'); + cy.url().should('include', 'login'); + }); + + it('Login without username', () => { + cy.get('.input[name="password"]').type('test'); + cy.get('.button[type="submit"]').click(); + cy.get('.application__view--auth-form').should('be.visible'); + cy.get('.application__content').should('not.exist'); + cy.get('.application__loading-overlay').should('not.exist'); + cy.get('.error').should('be.visible'); + }); + + it('Login without password', () => { + cy.get('.input[name="username"]').type('test'); + cy.get('.button[type="submit"]').click(); + cy.get('.application__view--auth-form').should('be.visible'); + cy.get('.application__content').should('not.exist'); + cy.get('.application__loading-overlay').should('not.exist'); + cy.get('.error').should('be.visible'); + }); + + it('Login, server error occurred', () => { + cy.get('.input[name="username"]').type('test'); + cy.get('.input[name="password"]').type('test'); + + cy.server(); + cy.route({ + method: 'POST', + url: 'http://127.0.0.1:4200/api/auth/authenticate', + response: {}, + status: 500, + }).as('verify-request'); + + cy.get('.button[type="submit"]').click(); + cy.get('.application__view--auth-form').should('be.visible'); + cy.get('.application__content').should('not.exist'); + cy.get('.application__loading-overlay').should('not.exist'); + cy.get('.error').should('be.visible'); + }); + + it('Clear', () => { + cy.get('.input[name="username"]').type('test').as('password'); + cy.get('.input[name="password"]').type('test').as('username'); + cy.get('.button__content').contains('Clear').parent().click(); + cy.get('@username').should('have.value', ''); + cy.get('@password').should('have.value', ''); + }); +}); diff --git a/cypress/integration/overview.spec.ts b/cypress/integration/overview.spec.ts new file mode 100644 index 000000000..3e050f53a --- /dev/null +++ b/cypress/integration/overview.spec.ts @@ -0,0 +1,38 @@ +context('Overview', () => { + beforeEach(() => { + cy.visit('http://127.0.0.1:4200/overview'); + cy.url().should('include', 'overview'); + cy.get('.application__loading-overlay').should('not.exist'); + }); + + it('Overview', () => { + cy.get('.application__view--auth-form').should('not.exist'); + cy.get('.application__content').should('be.visible'); + cy.get('.sidebar__actions').should('be.visible'); + cy.get('.view--torrent-list').should('be.visible'); + }); + + it('Switch theme', () => { + cy.get('.icon--theme-switch').should('be.visible'); + cy.get('.icon--theme-switch').parent().click(); + cy.screenshot('dark'); + cy.get('.icon--theme-switch').parent().click(); + cy.screenshot('light'); + }); + + it('Tooltip', () => { + cy.get('.icon--settings').should('be.visible'); + cy.get('.icon--settings').trigger('mouseover'); + cy.get('.tooltip__content').contains('Settings').should('exist'); + cy.get('.tooltip__content').contains('Settings').parent().should('have.class', 'is-open'); + cy.get('.icon--settings').trigger('mouseout'); + cy.get('.tooltip__content').contains('Settings').parent().should('not.have.class', 'is-open'); + + cy.get('.icon--add').should('be.visible'); + cy.get('.icon--add').trigger('mouseover'); + cy.get('.tooltip__content').contains('Add Torrent').should('exist'); + cy.get('.tooltip__content').contains('Add Torrent').parent().should('have.class', 'is-open'); + cy.get('.icon--add').trigger('mouseout'); + cy.get('.tooltip__content').contains('Add Torrent').parent().should('not.have.class', 'is-open'); + }); +}); diff --git a/cypress/integration/register.spec.ts b/cypress/integration/register.spec.ts new file mode 100644 index 000000000..ceb0d35de --- /dev/null +++ b/cypress/integration/register.spec.ts @@ -0,0 +1,135 @@ +import {AuthAuthenticationResponse} from '../../shared/schema/api/auth'; +import {AccessLevel} from '../../shared/schema/constants/Auth'; + +context('Register', () => { + beforeEach(() => { + cy.server(); + cy.route({ + method: 'GET', + url: 'http://127.0.0.1:4200/api/auth/verify?*', + response: {initialUser: true}, + status: 200, + }).as('verify-request'); + cy.visit('http://127.0.0.1:4200/register'); + cy.url().should('include', 'register'); + }); + + it('Client selection menu', () => { + cy.get('.select').click(); + cy.get('.context-menu').should('be.visible'); + cy.get('.select__item').contains('qBittorrent').click(); + cy.get('.context-menu').should('not.exist'); + cy.get('.input[name="client"]').should('have.value', 'qBittorrent'); + cy.get('.input--text[name="url"]').should('be.visible'); + cy.get('.input--text[name="qbt-username"]').should('be.visible'); + cy.get('.input--text[name="qbt-password"]').should('be.visible'); + }); + + it('Connection type selection', () => { + cy.get('.toggle-input__label').contains('TCP').click(); + cy.get('.toggle-input__element[value="tcp"]').should('be.checked'); + cy.get('.toggle-input__element[value="socket"]').should('not.be.checked'); + cy.get('.input--text[name="host"]').should('be.visible'); + cy.get('.input--text[name="port"]').should('be.visible'); + cy.get('.input--text[name="socket"]').should('not.exist'); + + cy.get('.toggle-input__label').contains('Socket').click(); + cy.get('.toggle-input__element[value="tcp"]').should('not.be.checked'); + cy.get('.toggle-input__element[value="socket"]').should('be.checked'); + cy.get('.input--text[name="host"]').should('not.exist'); + cy.get('.input--text[name="port"]').should('not.exist'); + cy.get('.input--text[name="socket"]').should('be.visible'); + }); + + it('Register without username', () => { + cy.get('.input[name="password"]').type('test'); + cy.get('.select').click(); + cy.get('.select__item').contains('rTorrent').click(); + cy.get('.toggle-input__label').contains('Socket').click(); + cy.get('.input--text[name="socket"]').type('/data/rtorrent.sock'); + cy.get('.button[type="submit"]').click(); + cy.get('.application__view--auth-form').should('be.visible'); + cy.get('.application__content').should('not.exist'); + cy.get('.application__loading-overlay').should('not.exist'); + cy.get('.error').should('be.visible'); + }); + + it('Register without password', () => { + cy.get('.input[name="username"]').type('test'); + cy.get('.select').click(); + cy.get('.select__item').contains('rTorrent').click(); + cy.get('.toggle-input__label').contains('TCP').click(); + cy.get('.input--text[name="host"]').type('127.0.0.1'); + cy.get('.input--text[name="port"]').type('5000'); + cy.get('.button[type="submit"]').click(); + cy.get('.application__view--auth-form').should('be.visible'); + cy.get('.application__content').should('not.exist'); + cy.get('.application__loading-overlay').should('not.exist'); + cy.get('.error').should('be.visible'); + }); + + it('Register without connection settings', () => { + cy.get('.input[name="username"]').type('test'); + cy.get('.input[name="password"]').type('test'); + cy.get('.button[type="submit"]').click(); + cy.get('.application__view--auth-form').should('be.visible'); + cy.get('.application__content').should('not.exist'); + cy.get('.application__loading-overlay').should('not.exist'); + cy.get('.error').should('be.visible'); + }); + + it('Register with socket connection settings, server error occurred', () => { + cy.get('.input[name="username"]').type('test'); + cy.get('.input[name="password"]').type('test'); + cy.get('.select').click(); + cy.get('.select__item').contains('rTorrent').click(); + cy.get('.toggle-input__label').contains('Socket').click(); + cy.get('.input--text[name="socket"]').type('/data/rtorrent.sock'); + + cy.server(); + cy.route({ + method: 'POST', + url: 'http://127.0.0.1:4200/api/auth/register', + response: {}, + status: 500, + }).as('register-request'); + + cy.get('.button[type="submit"]').click(); + + cy.get('.application__view--auth-form').should('be.visible'); + cy.get('.application__content').should('not.exist'); + cy.get('.application__loading-overlay').should('not.exist'); + + cy.get('.error').should('be.visible'); + }); + + it('Register with qBittorrent connection settings', () => { + cy.get('.input[name="username"]').type('test'); + cy.get('.input[name="password"]').type('test'); + cy.get('.select').click(); + cy.get('.select__item').contains('qBittorrent').click(); + cy.get('.input--text[name="url"]').type('http://127.0.0.1:8080'); + cy.get('.input--text[name="qbt-username"]').type('admin'); + cy.get('.input--text[name="qbt-password"]').type('adminadmin'); + + cy.server(); + + const response: AuthAuthenticationResponse = { + success: true, + username: 'test', + level: AccessLevel.ADMINISTRATOR, + }; + + cy.route({ + method: 'POST', + url: 'http://127.0.0.1:4200/api/auth/register', + response, + status: 200, + }).as('register-request'); + + cy.get('.button[type="submit"]').click(); + + cy.get('.application__view--auth-form').should('not.exist'); + cy.get('.application__content').should('be.visible'); + }); +}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 000000000..8dd144a6c --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,21 @@ +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +/** + * @type {Cypress.PluginConfig} + */ +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +}; diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 000000000..ca4d256f3 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,25 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 000000000..37a498fb5 --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 000000000..0363ab7c5 --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["es5", "dom"], + "types": ["cypress"] + }, + "include": ["**/*.ts"], + "resolveJsonModule": true +} diff --git a/fixtures/multi.torrent b/fixtures/multi.torrent new file mode 100644 index 000000000..a04a4533e Binary files /dev/null and b/fixtures/multi.torrent differ diff --git a/fixtures/single.torrent b/fixtures/single.torrent new file mode 100644 index 000000000..a25d2fb10 Binary files /dev/null and b/fixtures/single.torrent differ diff --git a/flood.png b/flood.png deleted file mode 100644 index 069493a0b..000000000 Binary files a/flood.png and /dev/null differ diff --git a/flood.svg b/flood.svg new file mode 100644 index 000000000..626b30da4 --- /dev/null +++ b/flood.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..ef87c4028 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +module.exports = { + verbose: true, + collectCoverage: true, + coverageProvider: 'v8', + projects: [ + '/server/.jest/auth.config.js', + '/server/.jest/rtorrent.config.js', + '/server/.jest/qbittorrent.config.js', + '/server/.jest/transmission.config.js', + ], +}; diff --git a/package-lock.json b/package-lock.json index 2d6722df4..55d431ac4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6836 +1,31573 @@ { "name": "flood", - "version": "1.0.0", - "lockfileVersion": 1, + "version": "4.3.1", + "lockfileVersion": 2, "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "requires": { - "@babel/highlight": "^7.0.0" + "packages": { + "": { + "version": "4.3.1", + "license": "GPL-3.0-only", + "dependencies": { + "geoip-country": "^4.0.46" + }, + "bin": { + "flood": "dist/index.js" + }, + "devDependencies": { + "@babel/core": "^7.12.10", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-decorators": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", + "@babel/preset-env": "^7.12.10", + "@babel/preset-react": "^7.12.10", + "@babel/preset-typescript": "^7.12.7", + "@formatjs/cli": "^2.13.15", + "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3", + "@types/bencode": "^2.0.0", + "@types/body-parser": "^1.19.0", + "@types/classnames": "^2.2.11", + "@types/clipboard": "^2.0.1", + "@types/compression": "^1.7.0", + "@types/content-disposition": "^0.5.3", + "@types/cookie-parser": "^1.4.2", + "@types/create-torrent": "^4.4.0", + "@types/d3": "^6.2.0", + "@types/debug": "^4.1.5", + "@types/express": "^4.17.9", + "@types/express-rate-limit": "^5.1.0", + "@types/fs-extra": "^9.0.5", + "@types/geoip-country": "^4.0.0", + "@types/http-errors": "^1.8.0", + "@types/jest": "^26.0.19", + "@types/jsonwebtoken": "^8.5.0", + "@types/morgan": "^1.9.2", + "@types/nedb": "^1.8.11", + "@types/node": "^12.19.9", + "@types/overlayscrollbars": "^1.12.0", + "@types/passport": "^1.0.4", + "@types/passport-jwt": "^3.0.3", + "@types/react": "^17.0.0", + "@types/react-dnd-multi-backend": "^6.0.0", + "@types/react-dom": "^17.0.0", + "@types/react-measure": "^2.0.6", + "@types/react-router-dom": "^5.1.6", + "@types/react-transition-group": "^4.4.0", + "@types/react-window": "^1.8.2", + "@types/spdy": "^3.4.4", + "@types/supertest": "^2.0.10", + "@types/tar": "^4.0.4", + "@typescript-eslint/eslint-plugin": "^4.9.1", + "@typescript-eslint/parser": "^4.9.1", + "@vercel/ncc": "^0.25.1", + "autoprefixer": "^10.1.0", + "axios": "^0.21.0", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.2.2", + "bencode": "^2.0.1", + "body-parser": "^1.19.0", + "case-sensitive-paths-webpack-plugin": "2.3.0", + "chalk": "^4.1.0", + "classnames": "^2.2.6", + "clipboard": "^2.0.6", + "compression": "^1.7.4", + "content-disposition": "^0.5.3", + "cookie-parser": "^1.4.5", + "create-torrent": "^4.4.2", + "css-loader": "^5.0.1", + "css-minimizer-webpack-plugin": "^1.1.5", + "d3-array": "^2.9.1", + "d3-scale": "^3.2.3", + "d3-selection": "^2.0.0", + "d3-shape": "^2.0.0", + "debug": "^4.3.1", + "eslint": "^7.15.0", + "eslint-config-airbnb": "^18.2.1", + "eslint-config-airbnb-typescript": "^12.0.0", + "eslint-config-prettier": "^7.0.0", + "eslint-config-react-app": "^6.0.0", + "eslint-import-resolver-webpack": "^0.13.0", + "eslint-plugin-flowtype": "5.2.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jest": "^24.1.3", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-react": "^7.21.5", + "eslint-plugin-react-hooks": "^4.2.0", + "eslint-webpack-plugin": "^2.4.1", + "express": "^4.17.1", + "express-rate-limit": "^5.2.3", + "fast-json-patch": "^3.0.0-1", + "fast-sort": "^2.2.0", + "feedsub": "^0.7.6", + "file-loader": "^6.2.0", + "form-data": "^3.0.0", + "frontmatter-markdown-loader": "^3.6.2", + "fs-extra": "^9.0.1", + "get-user-locale": "^1.4.0", + "hash-wasm": "^4.4.1", + "html-webpack-plugin": "^5.0.0-alpha.10", + "http-errors": "^1.8.0", + "jest": "^26.6.3", + "js-file-download": "^0.4.12", + "jsonwebtoken": "^8.5.1", + "lodash": "^4.17.20", + "mini-css-extract-plugin": "^1.3.3", + "mobx": "^6.0.4", + "mobx-react": "^7.0.5", + "morgan": "^1.10.0", + "nedb": "^1.8.0", + "nedb-promises": "^4.1.0", + "node-sass": "^5.0.0", + "overlayscrollbars": "^1.13.0", + "overlayscrollbars-react": "^0.2.2", + "passport": "^0.4.1", + "passport-jwt": "^4.0.0", + "postcss": "^8.2.1", + "postcss-loader": "^4.1.0", + "prettier": "^2.2.1", + "promise": "^8.1.0", + "query-string": "^6.13.7", + "react": "^17.0.1", + "react-dev-utils": "^11.0.1", + "react-dnd": "^11.1.3", + "react-dnd-html5-backend": "^11.1.3", + "react-dnd-multi-backend": "^6.0.2", + "react-dnd-touch-backend": "^11.1.3", + "react-dom": "^17.0.1", + "react-dropzone": "^11.2.4", + "react-intl": "^5.10.6", + "react-measure": "^2.5.2", + "react-refresh": "^0.9.0", + "react-router": "^5.2.0", + "react-router-dom": "^5.2.0", + "react-transition-group": "^4.4.1", + "react-use": "^15.3.4", + "react-window": "^1.8.6", + "ress": "^3.0.0", + "sanitize-filename": "^1.6.3", + "sass-loader": "^10.1.0", + "saxen": "^8.1.2", + "source-map-loader": "^1.1.3", + "spdy": "^4.0.2", + "style-loader": "^2.0.0", + "supertest": "^6.0.1", + "tar": "^6.0.5", + "terser-webpack-plugin": "^5.0.3", + "ts-jest": "^26.4.4", + "ts-node-dev": "^1.1.1", + "typed-emitter": "^1.3.1", + "typescript": "^4.1.3", + "url-loader": "^4.1.1", + "use-query-params": "^1.1.9", + "webpack": "^5.10.1", + "webpack-dev-server": "^4.0.0-beta.0", + "webpackbar": "^5.0.0-3", + "xmlrpc": "^1.3.2", + "yargs": "^16.2.0", + "zod": "^1.11.11" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0" } }, - "@babel/core": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz", - "integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helpers": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4", + "node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", + "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==", + "dev": true + }, + "node_modules/@babel/core": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", + "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.10", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", "semver": "^5.4.1", "source-map": "^0.5.0" }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", - "requires": { - "@babel/types": "^7.4.4", + "node_modules/@babel/generator": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", + "dev": true, + "dependencies": { + "@babel/types": "^7.12.10", "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "source-map": "^0.5.0" } }, - "@babel/helper-annotate-as-pure": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.4.tgz", - "integrity": "sha512-2BQmQgECKzYKFPpiycoF9tlb5HA4lrVyAmLLVK177EcQAqjVLciUb2/R+n1boQ9y5ENV3uz2ZqiNw7QMBBw1Og==", - "requires": { - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz", + "integrity": "sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.12.10" } }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.4.tgz", - "integrity": "sha512-Biq/d/WtvfftWZ9Uf39hbPBYDUo986m5Bb4zhkeYDGUllF43D+nUe5M6Vuo6/8JDK/0YX/uBdeoQpyaNhNugZQ==", - "requires": { - "@babel/helper-explode-assignable-expression": "^7.7.4", - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" } }, - "@babel/helper-builder-react-jsx": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.7.4.tgz", - "integrity": "sha512-kvbfHJNN9dg4rkEM4xn1s8d1/h6TYNvajy9L1wx4qLn9HFg0IkTsQi4rfBe92nxrPUFcMsHoMV+8rU7MJb3fCA==", - "requires": { - "@babel/types": "^7.7.4", - "esutils": "^2.0.0" - }, + "node_modules/@babel/helper-builder-react-jsx": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz", + "integrity": "sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/types": "^7.10.4" } }, - "@babel/helper-call-delegate": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.7.4.tgz", - "integrity": "sha512-8JH9/B7J7tCYJ2PpWVpw9JhPuEVHztagNVuQAFBVFYluRMlpG7F1CgKEgGeL6KFqcsIa92ZYVj6DSc0XwmN1ZA==", - "requires": { - "@babel/helper-hoist-variables": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-builder-react-jsx-experimental": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.10.tgz", + "integrity": "sha512-3Kcr2LGpL7CTRDTTYm1bzeor9qZbxbvU2AxsLA6mUG9gYarSfIKMK0UlU+azLWI+s0+BH768bwyaziWB2NOJlQ==", + "dev": true, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "@babel/helper-annotate-as-pure": "^7.12.10", + "@babel/helper-module-imports": "^7.12.5", + "@babel/types": "^7.12.10" } }, - "@babel/helper-create-class-features-plugin": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.7.4.tgz", - "integrity": "sha512-l+OnKACG4uiDHQ/aJT8dwpR+LhCJALxL0mJ6nzjB25e5IPwqV1VOsY7ah6UB1DG+VOXAIMtuC54rFJGiHkxjgA==", - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-member-expression-to-functions": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4" - }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", + "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", + "dev": true, "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/compat-data": "^7.12.5", + "@babel/helper-validator-option": "^7.12.1", + "browserslist": "^4.14.5", + "semver": "^5.5.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.4.tgz", - "integrity": "sha512-Mt+jBKaxL0zfOIWrfQpnfYCN7/rS6GKx6CCCfuoqVVd+17R8zNDlzVYmIi9qyb2wOk002NsmSTDymkIygDUH7A==", - "requires": { - "@babel/helper-regex": "^7.4.4", - "regexpu-core": "^4.6.0" + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", + "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/helper-define-map": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.7.4.tgz", - "integrity": "sha512-v5LorqOa0nVQUvAUTUF3KPastvUt/HzByXNamKQ6RdJRTV7j8rLL+WB5C/MzzWAwOomxDhYFb1wLLxHqox86lg==", - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/types": "^7.7.4", - "lodash": "^4.17.13" + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", + "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "regexpu-core": "^4.7.1" }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-map": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "dev": true, "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" } }, - "@babel/helper-explode-assignable-expression": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.4.tgz", - "integrity": "sha512-2/SicuFrNSXsZNBxe5UGdLr+HZg+raWBLE9vC98bdYOKX/U6PY0mdGlYUJdtTDPSU0Lw0PNbKKDpwYHJLn2jLg==", - "requires": { - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", + "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", + "dev": true, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "@babel/types": "^7.12.1" } }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" + "node_modules/@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "requires": { - "@babel/types": "^7.0.0" + "node_modules/@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "dependencies": { + "@babel/types": "^7.10.4" } }, - "@babel/helper-hoist-variables": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.4.tgz", - "integrity": "sha512-wQC4xyvc1Jo/FnLirL6CEgPgPCa8M74tOdjWpRhQYapz5JC7u3NYU1zCVoVAGCE3EaIP9T1A3iW0WLJ+reZlpQ==", - "requires": { - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", + "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.10.4" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.4.tgz", - "integrity": "sha512-9KcA1X2E3OjXl/ykfMMInBK+uVdfIVakVe7W7Lg3wfXUNyS3Q1HWLFRwZIjhqiCGbslummPDnmb7vIekS0C1vw==", - "requires": { - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.12.7" } }, - "@babel/helper-module-imports": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz", - "integrity": "sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==", - "requires": { - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.12.5" } }, - "@babel/helper-module-transforms": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.4.tgz", - "integrity": "sha512-ehGBu4mXrhs0FxAqN8tWkzF8GSIGAiEumu4ONZ/hD9M88uHcD+Yu2ttKfOCgwzoesJOJrtQh7trI5YPbRtMmnA==", - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-simple-access": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4", - "lodash": "^4.17.13" - }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, "dependencies": { - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.4.tgz", - "integrity": "sha512-VB7gWZ2fDkSuqW6b1AKXkJWO5NyNI3bFL/kK79/30moK57blr6NbH8xcl2XcKCwOmJosftWunZqfO84IGq3ZZg==", - "requires": { - "@babel/types": "^7.7.4" - }, + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.7.tgz", + "integrity": "sha512-I5xc9oSJ2h59OwyUqjv95HRyzxj53DAubUERgQMrpcCEYQyToeHA+NEcUEsVWB4j53RDeskeBJ0SgRAYHDBckw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.12.7" } }, - "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==" + "node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true }, - "@babel/helper-regex": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz", - "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==", - "requires": { - "lodash": "^4.17.13" + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", + "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/types": "^7.12.1" } }, - "@babel/helper-remap-async-to-generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.4.tgz", - "integrity": "sha512-Sk4xmtVdM9sA/jCI80f+KS+Md+ZHIpjuqmYPk1M7F/upHou5e4ReYmExAiu6PVe65BhJPZA2CY9x9k4BqE5klw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-wrap-function": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "dev": true, "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - } - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, - "@babel/helper-replace-supers": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.7.4.tgz", - "integrity": "sha512-pP0tfgg9hsZWo5ZboYGuBn/bbYT/hdLPVSS4NMmiRJdwWhP0IznPwN9AE1JwyGsjSPLC364I0Qh5p+EPkGPNpg==", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "@babel/types": "^7.12.1" } }, - "@babel/helper-simple-access": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.7.4.tgz", - "integrity": "sha512-zK7THeEXfan7UlWsG2A6CI/L9jVnI5+xxKZOdej39Y0YtDYKx9raHk5F2EtK9K8DHRTihYwg20ADt9S36GR78A==", - "requires": { - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "dev": true, "dependencies": { - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.12.1" } }, - "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", - "requires": { - "@babel/types": "^7.4.4" + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.11.0" } }, - "@babel/helper-wrap-function": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.7.4.tgz", - "integrity": "sha512-VsfzZt6wmsocOaVU0OokwrIytHND55yvyT4BPB9AIIgwr8+x7617hetdJTsuGwygN5RC6mxA9EJztTjuwm2ofg==", - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz", + "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==", + "dev": true + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", + "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", + "dev": true, "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - } - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, - "@babel/helpers": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz", - "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==", - "requires": { - "@babel/template": "^7.7.4", - "@babel/traverse": "^7.7.4", - "@babel/types": "^7.7.4" - }, + "node_modules/@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, "dependencies": { - "@babel/generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.4.tgz", - "integrity": "sha512-m5qo2WgdOJeyYngKImbkyQrnUN1mPceaG5BV+G0E3gWsa4l/jCSryWJdM2x8OuGAOyh+3d5pVYfZWCiNFtynxg==", - "requires": { - "@babel/types": "^7.7.4", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/traverse": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", - "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "requires": { - "@babel/highlight": "^7.0.0" - } - } - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "requires": { + "node_modules/@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" } }, - "@babel/parser": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.4.tgz", - "integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==" - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.4.tgz", - "integrity": "sha512-1ypyZvGRXriY/QP668+s8sFr2mqinhkRDMPSQLNghCQE+GAkFtp+wkHVvg2+Hdki8gwP+NFzJBJ/N1BfzCCDEw==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.4", - "@babel/plugin-syntax-async-generators": "^7.7.4" + "node_modules/@babel/highlight/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/plugin-proposal-class-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.4.tgz", - "integrity": "sha512-EcuXeV4Hv1X3+Q1TsuOmyyxeTRiSqurGJ26+I/FW1WbymmRRapVORm6x1Zl3iDIHyRxEs+VXWp6qnlcfcJSbbw==", + "node_modules/@babel/highlight/node_modules/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": { - "@babel/helper-create-class-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.7.4.tgz", - "integrity": "sha512-StH+nGAdO6qDB1l8sZ5UBV8AC3F2VW2I8Vfld73TMKyptMU9DY5YsJAS8U81+vEtxcH3Y/La0wG0btDrhpnhjQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-dynamic-import": "^7.7.4" + "node_modules/@babel/highlight/node_modules/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, + "dependencies": { + "color-name": "1.1.3" } }, - "@babel/plugin-proposal-json-strings": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.7.4.tgz", - "integrity": "sha512-wQvt3akcBTfLU/wYoqm/ws7YOAQKu8EVJEvHip/mzkNtjaclQoCCIqKXFP5/eyfnfbQCDV3OLRIK3mIVyXuZlw==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.7.4" + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/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, + "engines": { + "node": ">=0.8.0" } }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.7.4.tgz", - "integrity": "sha512-rnpnZR3/iWKmiQyJ3LKJpSwLDcX/nSXhdLk4Aq/tXOApIvyu7qoabrige0ylsAJffaUC51WiBu209Q0U+86OWQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.7.4" + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" } }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.7.4.tgz", - "integrity": "sha512-DyM7U2bnsQerCQ+sejcTNZh8KQEUuC3ufzdnVnSiUv/qoGJp2Z3hanKL18KDhsBT5Wj6a7CMT5mdyCNJsEaA9w==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.7.4" + "node_modules/@babel/highlight/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.4.tgz", - "integrity": "sha512-cHgqHgYvffluZk85dJ02vloErm3Y6xtH+2noOBOJ2kXOJH3aVCDnj5eR/lVNlTnYu4hndAPJD3rTFjW3qee0PA==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/parser": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "@babel/plugin-syntax-async-generators": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.7.4.tgz", - "integrity": "sha512-Li4+EjSpBgxcsmeEF8IFcfV/+yJGxHXDirDkEoyFjumuwbmfCVHUt0HuowD/iGM7OhIRyXJH9YXxqiH6N815+g==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz", + "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.12.1", + "@babel/plugin-syntax-async-generators": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.7.4.tgz", - "integrity": "sha512-jHQW0vbRGvwQNgyVxwDh4yuXu4bH1f5/EICJLAhl1SblLs2CDhrsmCk+v5XLdE9wxtAFRyxx+P//Iw+a5L/tTg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", + "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-json-strings": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.7.4.tgz", - "integrity": "sha512-QpGupahTQW1mHRXddMG5srgpHWqRLwJnJZKXTigB9RPFCCGbDGCgBeM/iC82ICXp414WeYx/tD54w7M2qRqTMg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.1.tgz", + "integrity": "sha512-knNIuusychgYN8fGJHONL0RbFxLGawhXOJNLBk75TniTsZZeA+wdkDuv6wp4lGwzQEKjZi6/WYtnb3udNPmQmQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-decorators": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-jsx": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.7.4.tgz", - "integrity": "sha512-wuy6fiMe9y7HeZBWXYCGt2RGxZOj0BImZ9EyXJVnVGBKO/Br592rbR3rtIQn0eQhAk9vqaKP5n8tVqEFBQMfLg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", + "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz", - "integrity": "sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", + "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.7.4.tgz", - "integrity": "sha512-4ZSuzWgFxqHRE31Glu+fEr/MirNZOMYmD/0BhBWyLyOOQz/gTAl7QmWm2hX1QxEIXsr2vkdlwxIzTyiYRC4xcQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", + "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.7.4.tgz", - "integrity": "sha512-wdsOw0MvkL1UIgiQ/IFr3ETcfv1xb8RMM0H9wbiDyLaJFyiDg5oZvDLCXosIXmFeIlweML5iOBXAkqddkYNizg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", + "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-typescript": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.7.4.tgz", - "integrity": "sha512-77blgY18Hud4NM1ggTA8xVT/dBENQf17OpiToSa2jSmEY3fWXD2jwrdVlO4kq5yzUTeF15WSQ6b4fByNvJcjpQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", + "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.7.4.tgz", - "integrity": "sha512-zUXy3e8jBNPiffmqkHRNDdZM2r8DWhCB7HhcoyZjiK1TxYEluLHAvQuYnTT+ARqRpabWqy/NHkO6e3MsYB5YfA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", + "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.4.tgz", - "integrity": "sha512-zpUTZphp5nHokuy8yLlyafxCJ0rSlFoSHypTUWgpdwoDXWQcseaect7cJ8Ppk6nunOM6+5rPMkod4OYKPR5MUg==", - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.4" + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.7.4.tgz", - "integrity": "sha512-kqtQzwtKcpPclHYjLK//3lH8OFsCDuDJBaFhVwf8kqdnF6MN4l618UDlcA7TfRs3FayrHj+svYnSX8MC9zmUyQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", + "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-block-scoping": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.7.4.tgz", - "integrity": "sha512-2VBe9u0G+fDt9B5OV5DQH4KBf5DoiNkwFKOz0TCvBWvdAN2rOykCTkrL+jTLxfCAm76l9Qo5OqL7HBOx2dWggg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "lodash": "^4.17.13" + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", + "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-classes": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.4.tgz", - "integrity": "sha512-sK1mjWat7K+buWRuImEzjNf68qrKcrddtpQo3swi9j7dUcG6y6R6+Di039QN2bD1dykeswlagupEmpOatFHHUg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-define-map": "^7.7.4", - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-optimise-call-expression": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4", - "@babel/helper-split-export-declaration": "^7.7.4", - "globals": "^11.1.0" - }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", + "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "dev": true, "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", - "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-computed-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.7.4.tgz", - "integrity": "sha512-bSNsOsZnlpLLyQew35rl4Fma3yKWqK3ImWMSC/Nc+6nGjC9s5NFWAer1YQ899/6s9HxO2zQC1WoFNfkOqRkqRQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", + "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-destructuring": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.7.4.tgz", - "integrity": "sha512-4jFMXI1Cu2aXbcXXl8Lr6YubCn6Oc7k9lLsu8v61TZh+1jny2BWmdtvY9zSUlLdGUvcy9DMAWyZEOqjsbeg/wA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.4.tgz", - "integrity": "sha512-mk0cH1zyMa/XHeb6LOTXTbG7uIJ8Rrjlzu91pUx/KS3JpcgaTDwMS8kM+ar8SLOvlL2Lofi4CGBAjCo3a2x+lw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.7.4.tgz", - "integrity": "sha512-g1y4/G6xGWMD85Tlft5XedGaZBCIVN+/P0bs6eabmcPP9egFleMAo65OOjlhcz1njpwagyY3t0nsQC9oTFegJA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.7.4.tgz", - "integrity": "sha512-MCqiLfCKm6KEA1dglf6Uqq1ElDIZwFuzz1WH5mTf8k2uQSxEJMbOIEh7IZv7uichr7PMfi5YVSrr1vz+ipp7AQ==", - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz", + "integrity": "sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-for-of": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.7.4.tgz", - "integrity": "sha512-zZ1fD1B8keYtEcKF+M1TROfeHTKnijcVQm0yO/Yu1f7qoDoxEIc/+GX6Go430Bg84eM/xwPFp0+h4EbZg7epAA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.4.tgz", - "integrity": "sha512-E/x09TvjHNhsULs2IusN+aJNRV5zKwxu1cpirZyRPw+FyyIKEHPXTsadj48bVpc1R5Qq1B5ZkzumuFLytnbT6g==", - "requires": { - "@babel/helper-function-name": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - }, + "node_modules/@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, "dependencies": { - "@babel/helper-function-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", - "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", - "requires": { - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/template": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/parser": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz", - "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==" - }, - "@babel/template": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", - "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.4", - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.7.4.tgz", - "integrity": "sha512-X2MSV7LfJFm4aZfxd0yLVFrEXAgPqYoDG53Br/tCKiKYfX0MjVjQeWPIhPHHsCqzwQANq+FLN786fF5rgLS+gw==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.7.4.tgz", - "integrity": "sha512-9VMwMO7i69LHTesL0RdGy93JU6a+qOPuvB4F4d0kR0zyVjJRVJRaoaGjhtki6SzQUu8yen/vxPKN6CWnCUw6bA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-modules-amd": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.7.4.tgz", - "integrity": "sha512-/542/5LNA18YDtg1F+QHvvUSlxdvjZoD/aldQwkq+E3WCkbEjNSN9zdrOXaSlfg3IfGi22ijzecklF/A7kVZFQ==", - "requires": { - "@babel/helper-module-transforms": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "babel-plugin-dynamic-import-node": "^2.3.0" + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.4.tgz", - "integrity": "sha512-k8iVS7Jhc367IcNF53KCwIXtKAH7czev866ThsTgy8CwlXjnKZna2VHwChglzLleYrcHz1eQEIJlGRQxB53nqA==", - "requires": { - "@babel/helper-module-transforms": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.7.4", - "babel-plugin-dynamic-import-node": "^2.3.0" + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.4.tgz", - "integrity": "sha512-y2c96hmcsUi6LrMqvmNDPBBiGCiQu0aYqpHatVVu6kD4mFEXKjyNxd/drc18XXAf9dv7UXjrZwBVmTTGaGP8iw==", - "requires": { - "@babel/helper-hoist-variables": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "babel-plugin-dynamic-import-node": "^2.3.0" + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-modules-umd": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.4.tgz", - "integrity": "sha512-u2B8TIi0qZI4j8q4C51ktfO7E3cQ0qnaXFI1/OXITordD40tt17g/sXqgNNCcMTcBFKrUPcGDx+TBJuZxLx7tw==", - "requires": { - "@babel/helper-module-transforms": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.4.tgz", - "integrity": "sha512-jBUkiqLKvUWpv9GLSuHUFYdmHg0ujC1JEYoZUfeOOfNydZXp1sXObgyPatpcwjWgsdBGsagWW0cdJpX/DO2jMw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4" + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-new-target": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.7.4.tgz", - "integrity": "sha512-CnPRiNtOG1vRodnsyGX37bHQleHE14B9dnnlgSeEs3ek3fHN1A1SScglTCg1sfbe7sRQ2BUcpgpTpWSfMKz3gg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-object-super": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.7.4.tgz", - "integrity": "sha512-ho+dAEhC2aRnff2JCA0SAK7V2R62zJd/7dmtoe7MHcso4C2mS+vZjn1Pb1pCVZvJs1mgsvv5+7sT+m3Bysb6eg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.4" + "node_modules/@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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-parameters": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.7.4.tgz", - "integrity": "sha512-VJwhVePWPa0DqE9vcfptaJSzNDKrWU/4FbYCjZERtmqEs05g3UMXnYMZoXja7JAJ7Y7sPZipwm/pGApZt7wHlw==", - "requires": { - "@babel/helper-call-delegate": "^7.7.4", - "@babel/helper-get-function-arity": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" - }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, "dependencies": { - "@babel/helper-get-function-arity": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", - "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", - "requires": { - "@babel/types": "^7.7.4" - } - }, - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-property-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.7.4.tgz", - "integrity": "sha512-MatJhlC4iHsIskWYyawl53KuHrt+kALSADLQQ/HkhTjX954fkxIEh4q5slL4oRAnsm/eDoZ4q0CIZpcqBuxhJQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz", + "integrity": "sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-react-display-name": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.7.4.tgz", - "integrity": "sha512-sBbIvqYkthai0X0vkD2xsAwluBp+LtNHH+/V4a5ydifmTtb8KOVOlrMIk/MYmIc4uTYDnjZUHQildYNo36SRJw==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", + "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-react-jsx": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.7.4.tgz", - "integrity": "sha512-LixU4BS95ZTEAZdPaIuyg/k8FiiqN9laQ0dMHB4MlpydHY53uQdWCUrwjLr5o6ilS6fAgZey4Q14XBjl5tL6xw==", - "requires": { - "@babel/helper-builder-react-jsx": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.7.4" + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", + "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-react-jsx-self": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.7.4.tgz", - "integrity": "sha512-PWYjSfqrO273mc1pKCRTIJXyqfc9vWYBax88yIhQb+bpw3XChVC7VWS4VwRVs63wFHKxizvGSd00XEr+YB9Q2A==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.7.4" + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", + "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-react-jsx-source": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.7.4.tgz", - "integrity": "sha512-5ZU9FnPhqtHsOXxutRtXZAzoEJwDaP32QcobbMP1/qt7NYcsCNK8XgzJcJfoEr/ZnzVvUNInNjIW22Z6I8p9mg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.7.4" + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz", + "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-regenerator": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.4.tgz", - "integrity": "sha512-e7MWl5UJvmPEwFJTwkBlPmqixCtr9yAASBqff4ggXTNicZiwbF8Eefzm6NVgfiBp7JdAGItecnctKTgH44q2Jw==", - "requires": { - "regenerator-transform": "^0.14.0" + "node_modules/@babel/plugin-transform-classes": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", + "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4", + "globals": "^11.1.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-reserved-words": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.7.4.tgz", - "integrity": "sha512-OrPiUB5s5XvkCO1lS7D8ZtHcswIC57j62acAnJZKqGGnHP+TIc/ljQSrgdX/QyOTdEK5COAhuc820Hi1q2UgLQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", + "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.7.4.tgz", - "integrity": "sha512-q+suddWRfIcnyG5YiDP58sT65AJDZSUhXQDZE3r04AuqD6d/XLaQPPXSBzP2zGerkgBivqtQm9XKGLuHqBID6Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", + "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-spread": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.7.4.tgz", - "integrity": "sha512-8OSs0FLe5/80cndziPlg4R0K6HcWSM0zyNhHhLsmw/Nc5MaA49cAsnoJ/t/YZf8qkG7fD+UjTRaApVDB526d7Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", + "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.7.4.tgz", - "integrity": "sha512-Ls2NASyL6qtVe1H1hXts9yuEeONV2TJZmplLONkMPUG158CtmnrzW5Q5teibM5UVOFjG0D3IC5mzXR6pPpUY7A==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0" + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", + "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-template-literals": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.7.4.tgz", - "integrity": "sha512-sA+KxLwF3QwGj5abMHkHgshp9+rRz+oY9uoRil4CyLtgEuE/88dpkeWgNk5qKVsJE9iSfly3nvHapdRiIS2wnQ==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", + "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.7.4.tgz", - "integrity": "sha512-KQPUQ/7mqe2m0B8VecdyaW5XcQYaePyl9R7IsKd+irzj6jvbhoGnRE+M0aNkyAzI07VfUQ9266L5xMARitV3wg==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", + "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-typescript": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.7.4.tgz", - "integrity": "sha512-X8e3tcPEKnwwPVG+vP/vSqEShkwODOEeyQGod82qrIuidwIrfnsGn11qPM1jBLF4MqguTXXYzm58d0dY+/wdpg==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-typescript": "^7.7.4" + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", + "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.4.tgz", - "integrity": "sha512-N77UUIV+WCvE+5yHw+oks3m18/umd7y392Zv7mYTpFqHtkpcc+QUz+gLJNTWVlWROIWeLqY0f3OjZxV5TcXnRw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0" + "node_modules/@babel/plugin-transform-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", + "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/preset-env": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.7.4.tgz", - "integrity": "sha512-Dg+ciGJjwvC1NIe/DGblMbcGq1HOtKbw8RLl4nIjlfcILKEOkWT/vRqPpumswABEBVudii6dnVwrBtzD7ibm4g==", - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.7.4", - "@babel/plugin-proposal-dynamic-import": "^7.7.4", - "@babel/plugin-proposal-json-strings": "^7.7.4", - "@babel/plugin-proposal-object-rest-spread": "^7.7.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.7.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.7.4", - "@babel/plugin-syntax-async-generators": "^7.7.4", - "@babel/plugin-syntax-dynamic-import": "^7.7.4", - "@babel/plugin-syntax-json-strings": "^7.7.4", - "@babel/plugin-syntax-object-rest-spread": "^7.7.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.7.4", - "@babel/plugin-syntax-top-level-await": "^7.7.4", - "@babel/plugin-transform-arrow-functions": "^7.7.4", - "@babel/plugin-transform-async-to-generator": "^7.7.4", - "@babel/plugin-transform-block-scoped-functions": "^7.7.4", - "@babel/plugin-transform-block-scoping": "^7.7.4", - "@babel/plugin-transform-classes": "^7.7.4", - "@babel/plugin-transform-computed-properties": "^7.7.4", - "@babel/plugin-transform-destructuring": "^7.7.4", - "@babel/plugin-transform-dotall-regex": "^7.7.4", - "@babel/plugin-transform-duplicate-keys": "^7.7.4", - "@babel/plugin-transform-exponentiation-operator": "^7.7.4", - "@babel/plugin-transform-for-of": "^7.7.4", - "@babel/plugin-transform-function-name": "^7.7.4", - "@babel/plugin-transform-literals": "^7.7.4", - "@babel/plugin-transform-member-expression-literals": "^7.7.4", - "@babel/plugin-transform-modules-amd": "^7.7.4", - "@babel/plugin-transform-modules-commonjs": "^7.7.4", - "@babel/plugin-transform-modules-systemjs": "^7.7.4", - "@babel/plugin-transform-modules-umd": "^7.7.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.7.4", - "@babel/plugin-transform-new-target": "^7.7.4", - "@babel/plugin-transform-object-super": "^7.7.4", - "@babel/plugin-transform-parameters": "^7.7.4", - "@babel/plugin-transform-property-literals": "^7.7.4", - "@babel/plugin-transform-regenerator": "^7.7.4", - "@babel/plugin-transform-reserved-words": "^7.7.4", - "@babel/plugin-transform-shorthand-properties": "^7.7.4", - "@babel/plugin-transform-spread": "^7.7.4", - "@babel/plugin-transform-sticky-regex": "^7.7.4", - "@babel/plugin-transform-template-literals": "^7.7.4", - "@babel/plugin-transform-typeof-symbol": "^7.7.4", - "@babel/plugin-transform-unicode-regex": "^7.7.4", - "@babel/types": "^7.7.4", - "browserslist": "^4.6.0", - "core-js-compat": "^3.1.1", - "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.5.0" - }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", + "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", - "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/preset-react": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.7.4.tgz", - "integrity": "sha512-j+vZtg0/8pQr1H8wKoaJyGL2IEk3rG/GIvua7Sec7meXVIvGycihlGMx5xcU00kqCJbwzHs18xTu3YfREOqQ+g==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-transform-react-display-name": "^7.7.4", - "@babel/plugin-transform-react-jsx": "^7.7.4", - "@babel/plugin-transform-react-jsx-self": "^7.7.4", - "@babel/plugin-transform-react-jsx-source": "^7.7.4" + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", + "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/preset-typescript": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.7.4.tgz", - "integrity": "sha512-rqrjxfdiHPsnuPur0jKrIIGQCIgoTWMTjlbWE69G4QJ6TIOVnnRnIJhUxNTL/VwDmEAVX08Tq3B1nirer5341w==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-transform-typescript": "^7.7.4" + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", + "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.12.1", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/runtime": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz", - "integrity": "sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==", - "requires": { - "regenerator-runtime": "^0.13.2" - }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", + "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", + "dev": true, "dependencies": { - "regenerator-runtime": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", - "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" - } + "@babel/helper-hoist-variables": "^7.10.4", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-identifier": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", + "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/traverse": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.4.tgz", - "integrity": "sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.11" - }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", + "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", + "dev": true, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "@babel/helper-create-regexp-features-plugin": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", + "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", + "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", "dev": true, - "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", - "dev": true - }, - "@phc/format": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@phc/format/-/format-0.5.0.tgz", - "integrity": "sha512-JWtZ5P1bfXU0bAtTzCpOLYHDXuxSVdtL/oqz4+xa97h8w9E5IlVN333wugXVFv8vZ1hbXObKQf1ptXmFFcMByg==", - "requires": { - "safe-buffer": "^5.1.2" + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", + "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/babel-types": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz", - "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==" - }, - "@types/babylon": { - "version": "6.16.5", - "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz", - "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==", - "requires": { - "@types/babel-types": "*" + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", + "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/classnames": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.9.tgz", - "integrity": "sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A==", - "dev": true - }, - "@types/css-modules-loader-core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", - "integrity": "sha512-LMbyf7THPqLCPHIXAj79v9Pa193MeOHgp1fBFRR6s6VvEVHUFIcM5bc/WttslOf+lao4TURNN1X1zfW5wr2CHQ==", - "requires": { - "postcss": "7.x.x" - }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz", + "integrity": "sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w==", + "dev": true, "dependencies": { - "postcss": { - "version": "7.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", - "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==" - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.10.tgz", + "integrity": "sha512-MM7/BC8QdHXM7Qc1wdnuk73R4gbuOpfrSUgfV/nODGc86sPY1tgmY2M9E9uAnf2e4DOIp8aKGWqgZfQxnTNGuw==", "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" + "dependencies": { + "@babel/helper-builder-react-jsx": "^7.10.4", + "@babel/helper-builder-react-jsx-experimental": "^7.12.10", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/history": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.3.tgz", - "integrity": "sha512-cS5owqtwzLN5kY+l+KgKdRJ/Cee8tlmQoGQuIE9tWnSmS3JMKzmxo2HIAk2wODMifGwO20d62xZQLYz+RLfXmw==", - "dev": true - }, - "@types/json-schema": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", - "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==" - }, - "@types/keymirror": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@types/keymirror/-/keymirror-0.1.1.tgz", - "integrity": "sha1-H+SObl4qlpyv7PDyRIWuI8cqvTA=", - "dev": true - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/node": { - "version": "11.15.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.15.2.tgz", - "integrity": "sha512-BqCU9uIFkUH9Sgo2uLYbmIiFB1T+VBiM8AI/El3LIAI5KzwtckeSG+3WOYZr9aMoX4UIvRFBWBeSaOu6hFue2Q==", - "dev": true - }, - "@types/prop-types": { - "version": "15.7.3", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" - }, - "@types/react": { - "version": "16.9.13", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.13.tgz", - "integrity": "sha512-LikzRslbiufJYHyzbHSW0GrAiff8QYLMBFeZmSxzCYGXKxi8m/1PHX+rsVOwhr7mJNq+VIu2Dhf7U6mjFERK6w==", - "requires": { - "@types/prop-types": "*", - "csstype": "^2.2.0" + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.7.tgz", + "integrity": "sha512-Rs3ETtMtR3VLXFeYRChle5SsP/P9Jp/6dsewBQfokDSzKJThlsuFcnzLTDRALiUmTC48ej19YD9uN1mupEeEDg==", + "dev": true, + "dependencies": { + "@babel/helper-builder-react-jsx-experimental": "^7.12.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/react-dom": { - "version": "16.9.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.4.tgz", - "integrity": "sha512-fya9xteU/n90tda0s+FtN5Ym4tbgxpq/hb/Af24dvs6uYnYn+fspaxw5USlw0R8apDNwxsqumdRoCoKitckQqw==", - "requires": { - "@types/react": "*" + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz", + "integrity": "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/react-intl": { - "version": "2.3.18", - "resolved": "https://registry.npmjs.org/@types/react-intl/-/react-intl-2.3.18.tgz", - "integrity": "sha512-DVNJs49zUxKRZng8VuILE886Yihdsf3yLr5vHk9zJrmF8SyRSK3sxNSvikAKxNkv9hX55XBTJShz6CkJnbNjgg==" - }, - "@types/react-router": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.3.tgz", - "integrity": "sha512-0gGhmerBqN8CzlnDmSgGNun3tuZFXerUclWkqEhozdLaJtfcJRUTGkKaEKk+/MpHd1KDS1+o2zb/3PkBUiv2qQ==", + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", + "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", "dev": true, - "requires": { - "@types/history": "*", - "@types/react": "*" + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/react-router-dom": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.5.tgz", - "integrity": "sha512-eFajSUASYbPHg2BDM1G8Btx+YqGgvROPIg6sBhl3O4kbDdYXdFdfrgQFf/pcBuQVObjfT9AL/dd15jilR5DIEA==", + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", + "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", "dev": true, - "requires": { - "@types/history": "*", - "@types/react": "*", - "@types/react-router": "*" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@types/react-transition-group": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.9.2.tgz", - "integrity": "sha512-5Fv2DQNO+GpdPZcxp2x/OQG/H19A01WlmpjVD9cKvVFmoVLOZ9LvBgSWG6pSXIU4og5fgbvGPaCV5+VGkWAEHA==", + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", + "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", "dev": true, - "requires": { - "@types/react": "*" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@typescript-eslint/eslint-plugin": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz", - "integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==", + "node_modules/@babel/plugin-transform-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", + "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "1.13.0", - "eslint-utils": "^1.3.1", - "functional-red-black-tree": "^1.0.1", - "regexpp": "^2.0.1", - "tsutils": "^3.7.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@typescript-eslint/experimental-utils": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", - "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "1.13.0", - "eslint-scope": "^4.0.0" + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", + "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@typescript-eslint/parser": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz", - "integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==", - "requires": { - "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "1.13.0", - "@typescript-eslint/typescript-estree": "1.13.0", - "eslint-visitor-keys": "^1.0.0" + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", + "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@typescript-eslint/typescript-estree": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", - "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", - "requires": { - "lodash.unescape": "4.0.1", - "semver": "5.5.0" - }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz", + "integrity": "sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==", + "dev": true, "dependencies": { - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" - } + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/ast": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz", + "integrity": "sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw==", "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-typescript": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", + "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.8.5" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", + "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "node_modules/@babel/preset-env": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.10.tgz", + "integrity": "sha512-Gz9hnBT/tGeTE2DBNDkD7BiWRELZt+8lSysHuDwmYXUIvtwZl0zI+D6mZgXZX0u8YBlLS4tmai9ONNY9tjRgRA==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" + "dependencies": { + "@babel/compat-data": "^7.12.7", + "@babel/helper-compilation-targets": "^7.12.5", + "@babel/helper-module-imports": "^7.12.5", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-proposal-async-generator-functions": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-dynamic-import": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.1", + "@babel/plugin-proposal-json-strings": "^7.12.1", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-numeric-separator": "^7.12.7", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-class-properties": "^7.12.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.12.1", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-async-to-generator": "^7.12.1", + "@babel/plugin-transform-block-scoped-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.1", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-computed-properties": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-dotall-regex": "^7.12.1", + "@babel/plugin-transform-duplicate-keys": "^7.12.1", + "@babel/plugin-transform-exponentiation-operator": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-function-name": "^7.12.1", + "@babel/plugin-transform-literals": "^7.12.1", + "@babel/plugin-transform-member-expression-literals": "^7.12.1", + "@babel/plugin-transform-modules-amd": "^7.12.1", + "@babel/plugin-transform-modules-commonjs": "^7.12.1", + "@babel/plugin-transform-modules-systemjs": "^7.12.1", + "@babel/plugin-transform-modules-umd": "^7.12.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", + "@babel/plugin-transform-new-target": "^7.12.1", + "@babel/plugin-transform-object-super": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-property-literals": "^7.12.1", + "@babel/plugin-transform-regenerator": "^7.12.1", + "@babel/plugin-transform-reserved-words": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/plugin-transform-sticky-regex": "^7.12.7", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/plugin-transform-typeof-symbol": "^7.12.10", + "@babel/plugin-transform-unicode-escapes": "^7.12.1", + "@babel/plugin-transform-unicode-regex": "^7.12.1", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.12.10", + "core-js-compat": "^3.8.0", + "semver": "^5.5.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "node_modules/@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" + "dependencies": { + "@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" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "node_modules/@babel/preset-react": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.10.tgz", + "integrity": "sha512-vtQNjaHRl4DUpp+t+g4wvTHsLQuye+n0H/wsXIZRn69oz/fvNC7gQ4IK73zGJBaxvHoxElDvnYCthMcT7uzFoQ==", "dev": true, - "requires": { - "@xtuc/long": "4.2.2" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-transform-react-display-name": "^7.12.1", + "@babel/plugin-transform-react-jsx": "^7.12.10", + "@babel/plugin-transform-react-jsx-development": "^7.12.7", + "@babel/plugin-transform-react-pure-annotations": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "node_modules/@babel/preset-typescript": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.12.7.tgz", + "integrity": "sha512-nOoIqIqBmHBSEgBXWR4Dv/XBehtIFcw9PqZw6rFYuKrzsZmOQm3PR5siLBnKZFEsDb03IegG8nSjU/iXXXYRmw==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-transform-typescript": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "node_modules/@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" + "dependencies": { + "regenerator-runtime": "^0.13.4" } }, - "@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "node_modules/@babel/runtime-corejs3": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", + "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" + "dependencies": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" } }, - "@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "node_modules/@babel/template": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" } }, - "@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "node_modules/@babel/traverse": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", - "@xtuc/long": "4.2.2" + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.10", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" } }, - "@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "node_modules/@babel/types": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", - "@xtuc/long": "4.2.2" + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" } }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "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 + "node_modules/@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "dependencies": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "bin": { + "watch": "cli.js" + }, + "engines": { + "node": ">=0.1.95" + } }, - "abab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", - "dev": true + "node_modules/@eslint/eslintrc": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", + "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "dependencies": { + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "accepts": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.6.tgz", - "integrity": "sha512-QsaoUD2dpVpjENy8JFpQnXP9vyzoZPmAoKrE3S6HtSB7qzSebkJNnmdY4p004FQUSSiHXPueENpoeuUW/7a8Ig==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.1" + "node_modules/@eslint/eslintrc/node_modules/import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" } }, - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" + "node_modules/@eslint/eslintrc/node_modules/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, + "engines": { + "node": ">=4" + } }, - "acorn-globals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", - "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", - "requires": { - "acorn": "^4.0.4" - }, + "node_modules/@eslint/eslintrc/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/@formatjs/cli": { + "version": "2.13.15", + "resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-2.13.15.tgz", + "integrity": "sha512-QyCS7hbge5VJ5lFNTrk1zvUW6Um+6gnKd+Vz7aSPr3fWvTBz3xfmdhacoHbC5dtJdiQOPCwX9x1Yy9VjnVfUAw==", + "dev": true, "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" - } + "@formatjs/ts-transformer": "2.12.10", + "@types/json-stable-stringify": "^1.0.32", + "@types/lodash": "^4.14.150", + "@types/loud-rejection": "^2.0.0", + "@types/node": "14", + "chalk": "^4.0.0", + "commander": "^6.1.0", + "fast-glob": "^3.2.4", + "fs-extra": "^9.0.0", + "intl-messageformat-parser": "6.0.18", + "json-stable-stringify": "^1.0.1", + "lodash": "^4.17.15", + "loud-rejection": "^2.2.0", + "tslib": "^2.0.1", + "typescript": "^4.0" + }, + "bin": { + "formatjs": "bin/formatjs" } }, - "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", + "node_modules/@formatjs/cli/node_modules/@types/node": { + "version": "14.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.11.tgz", + "integrity": "sha512-BJ97wAUuU3NUiUCp44xzUFquQEvnk1wu7q4CMEUYKJWjdkr0YWYDsm4RFtAvxYsNjLsKcrFt6RvK8r+mnzMbEQ==", "dev": true }, - "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", - "dev": true + "node_modules/@formatjs/ecma402-abstract": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz", + "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + } }, - "add-px-to-style": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/add-px-to-style/-/add-px-to-style-1.0.0.tgz", - "integrity": "sha1-0ME1RB+oAUqBN5BFMQlvZ/KPJjo=" + "node_modules/@formatjs/intl": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-1.4.10.tgz", + "integrity": "sha512-CwbOmAnM2QKBUs6Eps1ry0YBe9nIQgQp9xQyxth/0BjJ8zRE3gIUzdNrLNCZ41nHuNPVFJRRIX79+yu5l+A56w==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.5.0", + "@formatjs/intl-datetimeformat": "3.1.0", + "@formatjs/intl-displaynames": "4.0.1", + "@formatjs/intl-listformat": "5.0.1", + "@formatjs/intl-relativetimeformat": "8.0.0", + "fast-memoize": "^2.5.2", + "intl-messageformat": "9.3.20", + "intl-messageformat-parser": "6.0.18", + "tslib": "^2.0.1" + } + }, + "node_modules/@formatjs/intl-datetimeformat": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/intl-datetimeformat/-/intl-datetimeformat-3.1.0.tgz", + "integrity": "sha512-XKyDQ3xFgZK2w8GE2v+zE0nk/JqGKFE0UxTI716mp/+OVuws+dbQPiORfSrJceH7E3ZkfGrvO0BB8sksQNsZ+w==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } }, - "address": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", - "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", - "dev": true + "node_modules/@formatjs/intl-displaynames": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-4.0.1.tgz", + "integrity": "sha512-vhG9y+F0BudHU9ev0O9Tc5Uwz/MAcCzbBzceSnjcoUMyLLfFN6GSPBvU6+ocxWsfjhu/yL5ja+doZdhwDcSXrA==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } }, - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "node_modules/@formatjs/intl-listformat": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-5.0.1.tgz", + "integrity": "sha512-x1gqI3xvTn8uTY0W+bL4ySW/5HFeQXkNNfsdoaRtX2b/HNa4fZoU1EaA6koAk9gUAWSR5Ofe1Ps49CXaMvwcTg==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" } }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + "node_modules/@formatjs/intl-relativetimeformat": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-8.0.0.tgz", + "integrity": "sha512-GKJvd2+Sx0BJqsKt2rBbkgGAwfBjKVnvlRTZQ+OhgSEOeRBHOtaub1jUx8ScQoS5Xe0RFLvTLL2LSnajg6EXkw==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } }, - "ajv-keywords": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", - "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==" + "node_modules/@formatjs/ts-transformer": { + "version": "2.12.10", + "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.12.10.tgz", + "integrity": "sha512-H8mtPQcyXxLo3GJGkNVj3ZlmebeqxQfVTIvGsdpE1oXKZ/SxKqvC7ZeHlbZUyXUEiRwdJ4Hfsgw1QzsmTJnicw==", + "dev": true, + "dependencies": { + "intl-messageformat-parser": "6.0.18", + "tslib": "^2.0.1", + "typescript": "^4.0" + }, + "peerDependencies": { + "ts-jest": "^26.4.0" + }, + "peerDependenciesMeta": { + "ts-jest": { + "optional": true + } + } }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "requires": { - "string-width": "^2.0.0" + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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 - }, - "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" - } - } + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">= 10.14.2" } }, - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true + "node_modules/@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } }, - "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 + "node_modules/@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true + "node_modules/@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "node_modules/@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "node-notifier": "^8.0.0" + } }, - "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==", - "requires": { - "color-convert": "^1.9.0" + "node_modules/@jest/reporters/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "node_modules/@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/source-map/node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@jest/source-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "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" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" } }, - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" - }, - "append-transform": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "node_modules/@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", "dev": true, - "requires": { - "default-require-extensions": "^1.0.0" + "dependencies": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + }, + "engines": { + "node": ">= 10.14.2" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + "node_modules/@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "node_modules/@jest/transform/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "argon2": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.24.1.tgz", - "integrity": "sha512-2S677iO18I+SQEUONkpvyagF9BJDYdiT82KqSMPQ2zP0oIYagVIPM0Y8T5pJ/4F4CrnN9PTCGA+ye1S0KupW3g==", - "requires": { - "@phc/format": "^0.5.0", - "node-addon-api": "^1.7.1", - "node-gyp-build": "^4.1.0" + "node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "aria-query": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", "dev": true, - "requires": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" + "engines": { + "node": ">= 8" } }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", "dev": true, - "requires": { - "arr-flatten": "^1.0.1" + "dependencies": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + "node_modules/@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", + "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=10" + } }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.3.tgz", + "integrity": "sha512-br5Qwvh8D2OQqSXpd1g/xqXKnK0r+Jz6qVKBbWmpUcrbGOxUrf39V5oZ1876084CGn18uMdR5uvPqBv9UqtBjQ==", + "dev": true, + "dependencies": { + "ansi-html": "^0.0.7", + "error-stack-parser": "^2.0.6", + "html-entities": "^1.2.1", + "native-url": "^0.2.6", + "schema-utils": "^2.6.5", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.x" + }, + "peerDependencies": { + "@types/webpack": "4.x", + "react-refresh": ">=0.8.3 <0.10.0", + "sockjs-client": "^1.4.0", + "type-fest": "^0.13.1", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/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, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@react-dnd/asap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.0.tgz", + "integrity": "sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ==", "dev": true }, - "array-find": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz", - "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", + "node_modules/@react-dnd/invariant": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", + "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==", "dev": true }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + "node_modules/@react-dnd/shallowequal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", + "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==", + "dev": true }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "node_modules/@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "node_modules/@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" + "dependencies": { + "@sinonjs/commons": "^1.7.0" } }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "node_modules/@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", "dev": true, - "requires": { - "array-uniq": "^1.0.1" + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true + "node_modules/@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true + "node_modules/@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true + "node_modules/@types/babel__traverse": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", + "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "node_modules/@types/bencode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/bencode/-/bencode-2.0.0.tgz", + "integrity": "sha512-ntDggX576d+MULpy9ApOy3OI9GqO86H+T9zEwYk3fdVaLi85M/1l+GVR/UWfITg9czcOO2SxZJyzyTOrI8UsFA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" + "node_modules/@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" } }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "node_modules/@types/classnames": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz", + "integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==", + "dev": true + }, + "node_modules/@types/clipboard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.1.tgz", + "integrity": "sha512-gJJX9Jjdt3bIAePQRRjYWG20dIhAgEqonguyHxXuqALxsoDsDLimihqrSg8fXgVTJ4KZCzkfglKtwsh/8dLfbA==", + "dev": true + }, + "node_modules/@types/compression": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.0.tgz", + "integrity": "sha512-3LzWUM+3k3XdWOUk/RO+uSjv7YWOatYq2QADJntK1pjkk4DfVP0KrIEPDnXRJxAAGKe0VpIPRmlINLDuCedZWw==", "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "dependencies": { + "@types/express": "*" } }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "node_modules/@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", "dev": true, - "requires": { - "util": "0.10.3" - }, "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } + "@types/node": "*" } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "node_modules/@types/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-P1bffQfhD3O4LW0ioENXUhZ9OIa0Zn+P7M+pWgkCKaT53wVLSq0mrKksCID/FGHpFhRSxRGhgrQmfhRuzwtKdg==", + "dev": true }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + "node_modules/@types/cookie-parser": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz", + "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "node_modules/@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", "dev": true }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "node_modules/@types/create-torrent": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/create-torrent/-/create-torrent-4.4.0.tgz", + "integrity": "sha512-ED4MMoZeSQvNt6IhIiCunXy27Yl25fXf3SENKX4FBU4d1dYDC/rF7wDGir8G829YjUln8vbw0Hqp3T1xWr3mKA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/d3": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-6.2.0.tgz", + "integrity": "sha512-XhQ6sCTu+CrFLqJMsg/uRPZQrt5FlCPjPE/wvsSBYoaOZ9C1chdJSS9+2oR8+Xtk6DKGewa7/UP5icJRwAryEA==", + "dev": true, + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-2.8.0.tgz", + "integrity": "sha512-Q0ubcGHAmCRPh90/hoYB4eKWhxYKUxphwSeQrlz2tiabQ8S9zqhaE2CZJtCaLH2cjqKcjr52WPvmOA7ha0O4ZA==", "dev": true }, - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "requires": { - "lodash": "^4.17.11" + "node_modules/@types/d3-axis": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-2.0.0.tgz", + "integrity": "sha512-gUdlEwGBLl3tXGiBnBNmNzph9W3bCfa4tBgWZD60Z1eDQKTY4zyCAcZ3LksignGfKawYatmDYcBdjJ5h/54sqA==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" } }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" + "node_modules/@types/d3-brush": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-2.1.0.tgz", + "integrity": "sha512-rLQqxQeXWF4ArXi81GlV8HBNwJw9EDpz0jcWvvzv548EDE4tXrayBTOHYi/8Q4FZ/Df8PGXFzxpAVQmJMjOtvQ==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "node_modules/@types/d3-chord": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-2.0.0.tgz", + "integrity": "sha512-3nHsLY7lImpZlM/hrPeDqqW2a+lRXXoHsG54QSurDGihZAIE/doQlohs0evoHrWOJqXyn4A4xbSVEtXnMEZZiw==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + "node_modules/@types/d3-color": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.1.tgz", + "integrity": "sha512-u7LTCL7RnaavFSmob2rIAJLNwu50i6gFwY9cHFr80BrQURYQBRkJ+Yv47nA3Fm7FeRhdWTiVTeqvSeOuMAOzBQ==", + "dev": true }, - "attr-accept": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz", - "integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==", - "requires": { - "core-js": "^2.5.0" - }, + "node_modules/@types/d3-contour": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-2.0.0.tgz", + "integrity": "sha512-PS9UO6zBQqwHXsocbpdzZFONgK1oRUgWtjjh/iz2vM06KaXLInLiKZ9e3OLBRerc1cU2uJYpO+8zOnb6frvCGQ==", + "dev": true, "dependencies": { - "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" - } + "@types/d3-array": "*", + "@types/geojson": "*" } }, - "autoprefixer": { - "version": "8.6.5", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.5.tgz", - "integrity": "sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==", - "requires": { - "browserslist": "^3.2.8", - "caniuse-lite": "^1.0.30000864", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^6.0.23", - "postcss-value-parser": "^3.2.3" - }, + "node_modules/@types/d3-delaunay": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-5.3.0.tgz", + "integrity": "sha512-gJYcGxLu0xDZPccbUe32OUpeaNtd1Lz0NYJtko6ZLMyG2euF4pBzrsQXms67LHZCDFzzszw+dMhSL/QAML3bXw==", + "dev": true + }, + "node_modules/@types/d3-dispatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-2.0.0.tgz", + "integrity": "sha512-Sh0KW6z/d7uxssD7K4s4uCSzlEG/+SP+U47q098NVdOfFvUKNTvKAIV4XqjxsUuhE/854ARAREHOxkr9gQOCyg==", + "dev": true + }, + "node_modules/@types/d3-drag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-2.0.0.tgz", + "integrity": "sha512-VaUJPjbMnDn02tcRqsHLRAX5VjcRIzCjBfeXTLGe6QjMn5JccB5Cz4ztMRXMJfkbC45ovgJFWuj6DHvWMX1thA==", + "dev": true, "dependencies": { - "browserslist": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", - "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", - "requires": { - "caniuse-lite": "^1.0.30000844", - "electron-to-chromium": "^1.3.47" - } - } + "@types/d3-selection": "*" } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "node_modules/@types/d3-dsv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-2.0.0.tgz", + "integrity": "sha512-wYqy7T8tQ/DmocwxmlPujllLI5fg3lb6/FrVVWkLUD+NsRV+kcE4nbRZg10G9yjJ8pK2ZXqu+VP5jQbN13uNRQ==", + "dev": true }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + "node_modules/@types/d3-ease": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-2.0.0.tgz", + "integrity": "sha512-6aZrTyX5LG+ptofVHf+gTsThLRY1nhLotJjgY4drYqk1OkJMu2UvuoZRlPw2fffjRHeYepue3/fxTufqKKmvsA==", + "dev": true }, - "axios": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", - "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", - "requires": { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" - }, + "node_modules/@types/d3-fetch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-2.0.0.tgz", + "integrity": "sha512-WnLepGtxepFfXRdPI8I5FTgNiHn9p4vMTTqaNCzJJfAswXx0rOY2jjeolzEU063em3iJmGZ+U79InnEeFOrCRw==", + "dev": true, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - } - }, - "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "@types/d3-dsv": "*" } }, - "axobject-query": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", - "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", + "node_modules/@types/d3-force": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-2.1.0.tgz", + "integrity": "sha512-LGDtC2YADu8OBniq9EBx/MOsXsMcJbEkmfSpXuz6oVdRamB+3CLCiq5EKFPEILGZQckkilGFq1ZTJ7kc289k+Q==", + "dev": true + }, + "node_modules/@types/d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-uagdkftxnGkO4pZw5jEYOM5ZnZOEsh7z8j11Qxk85UkB2RzfUUxRl7R9VvvJZHwKn8l+x+rpS77Nusq7FkFmIg==", + "dev": true + }, + "node_modules/@types/d3-geo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-2.0.0.tgz", + "integrity": "sha512-DHHgYXW36lnAEQMYU2udKVOxxljHrn2EdOINeSC9jWCAXwOnGn7A19B8sNsHqgpu4F7O2bSD7//cqBXD3W0Deg==", "dev": true, - "requires": { - "ast-types-flow": "0.0.7" + "dependencies": { + "@types/geojson": "*" } }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "node_modules/@types/d3-hierarchy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-2.0.0.tgz", + "integrity": "sha512-YxdskUvwzqggpnSnDQj4KVkicgjpkgXn/g/9M9iGsiToLS3nG6Ytjo1FoYhYVAAElV/fJBGVL3cQ9Hb7tcv+lw==", + "dev": true + }, + "node_modules/@types/d3-interpolate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-2.0.0.tgz", + "integrity": "sha512-Wt1v2zTlEN8dSx8hhx6MoOhWQgTkz0Ukj7owAEIOF2QtI0e219paFX9rf/SLOr/UExWb1TcUzatU8zWwFby6gg==", "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } + "@types/d3-color": "*" } }, - "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "node_modules/@types/d3-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.0.tgz", + "integrity": "sha512-tXcR/9OtDdeCIsyl6eTNHC3XOAOdyc6ceF3QGBXOd9jTcK+ex/ecr00p9L9362e/op3UEPpxrToi1FHrtTSj7Q==", + "dev": true + }, + "node_modules/@types/d3-polygon": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-2.0.0.tgz", + "integrity": "sha512-fISnMd8ePED1G4aa4V974Jmt+ajHSgPoxMa2D0ULxMybpx0Vw4WEzhQEaMIrL3hM8HVRcKTx669I+dTy/4PhAw==", + "dev": true + }, + "node_modules/@types/d3-quadtree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-2.0.0.tgz", + "integrity": "sha512-YZuJuGBnijD0H+98xMJD4oZXgv/umPXy5deu3IimYTPGH3Kr8Th6iQUff0/6S80oNBD7KtOuIHwHUCymUiRoeQ==", + "dev": true + }, + "node_modules/@types/d3-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-2.2.0.tgz", + "integrity": "sha512-Hjfj9m68NmYZzushzEG7etPvKH/nj9b9s9+qtkNG3/dbRBjQZQg1XS6nRuHJcCASTjxXlyXZnKu2gDxyQIIu9A==", + "dev": true + }, + "node_modules/@types/d3-scale": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.2.1.tgz", + "integrity": "sha512-j+FryQSVk3GHLqjOX/RsHwGHg4XByJ0xIO1ASBTgzhE9o1tgeV4kEWLOzMzJRembKalflk5F03lEkM+4V6LDrQ==", "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - }, "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" - } - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "@types/d3-time": "*" } }, - "babel-eslint": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz", - "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - }, + "node_modules/@types/d3-scale-chromatic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-2.0.0.tgz", + "integrity": "sha512-Y62+2clOwZoKua84Ha0xU77w7lePiaBoTjXugT4l8Rd5LAk+Mn/ZDtrgs087a+B5uJ3jYUHHtKw5nuEzp0WBHw==", + "dev": true + }, + "node_modules/@types/d3-selection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-2.0.0.tgz", + "integrity": "sha512-EF0lWZ4tg7oDFg4YQFlbOU3936e3a9UmoQ2IXlBy1+cv2c2Pv7knhKUzGlH5Hq2sF/KeDTH1amiRPey2rrLMQA==", + "dev": true + }, + "node_modules/@types/d3-shape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.0.0.tgz", + "integrity": "sha512-NLzD02m5PiD1KLEDjLN+MtqEcFYn4ZL9+Rqc9ZwARK1cpKZXd91zBETbe6wpBB6Ia0D0VZbpmbW3+BsGPGnCpA==", + "dev": true, "dependencies": { - "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", - "requires": { - "path-parse": "^1.0.6" - } - } + "@types/d3-path": "^1" } }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "node_modules/@types/d3-shape/node_modules/@types/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==", + "dev": true + }, + "node_modules/@types/d3-time": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.0.0.tgz", + "integrity": "sha512-Abz8bTzy8UWDeYs9pCa3D37i29EWDjNTjemdk0ei1ApYVNqulYlGUKip/jLOpogkPSsPz/GvZCYiC7MFlEk0iQ==", + "dev": true + }, + "node_modules/@types/d3-time-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UpLg1mn/8PLyjr+J/JwdQJM/GzysMvv2CS8y+WYAL5K0+wbvXv/pPSLEfdNaprCZsGcXTxPsFMy8QtkYv9ueew==", + "dev": true + }, + "node_modules/@types/d3-timer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-2.0.0.tgz", + "integrity": "sha512-l6stHr1VD1BWlW6u3pxrjLtJfpPZq9I3XmKIQtq7zHM/s6fwEtI1Yn6Sr5/jQTrUDCC5jkS6gWqlFGCDArDqNg==", + "dev": true + }, + "node_modules/@types/d3-transition": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-2.0.0.tgz", + "integrity": "sha512-UJDzI98utcZQUJt3uIit/Ho0/eBIANzrWJrTmi4+TaKIyWL2iCu7ShP0o4QajCskhyjOA7C8+4CE3b1YirTzEQ==", "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - }, "dependencies": { - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - } + "@types/d3-selection": "*" } }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "node_modules/@types/d3-zoom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-2.0.0.tgz", + "integrity": "sha512-daL0PJm4yT0ISTGa7p2lHX0kvv9FO/IR1ooWbHR/7H4jpbaKiLux5FslyS/OvISPiJ5SXb4sOqYhO6fMB6hKRw==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" } }, - "babel-jest": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.3.tgz", - "integrity": "sha512-BgSjmtl3mW3i+VeVHEr9d2zFSAT66G++pJcHQiUjd00pkW+voYXFctIm/indcqOWWXw5a1nUpR1XWszD9fJ1qg==", + "node_modules/@types/debug": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz", + "integrity": "sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw==", "dev": true, - "requires": { - "babel-plugin-istanbul": "^4.1.5", - "babel-preset-jest": "^22.4.3" + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" } }, - "babel-loader": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz", - "integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==", - "requires": { - "find-cache-dir": "^2.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "pify": "^4.0.1" + "node_modules/@types/eslint-scope": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", + "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" } }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "node_modules/@types/estree": { + "version": "0.0.45", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", + "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.9.tgz", + "integrity": "sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "requires": { - "object.assign": "^4.1.0" + "node_modules/@types/express-rate-limit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.0.tgz", + "integrity": "sha512-vmg7S3hUnfFmp06V01DrTB41mbQYXMV/F4aF5KKnfCIeSlnizatXaqO9UgR6LvNEEd3eMpuUTLxR6nv3d4hZ6g==", + "dev": true, + "dependencies": { + "@types/express": "*" } }, - "babel-plugin-istanbul": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz", - "integrity": "sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==", + "node_modules/@types/express-serve-static-core": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.14.tgz", + "integrity": "sha512-uFTLwu94TfUFMToXNgRZikwPuZdOtDgs3syBtAIr/OXorL1kJqUJT9qCLnRZ5KBOWfZQikQ2xKgR2tnDj1OgDA==", "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.13.0", - "find-up": "^2.1.0", - "istanbul-lib-instrument": "^1.10.1", - "test-exclude": "^4.2.1" - }, "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - } + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" } }, - "babel-plugin-jest-hoist": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz", - "integrity": "sha512-DUvGfYaAIlkdnygVIEl0O4Av69NtuQWcrjMOv6DODPuhuGLDnbsARz3AwiiI/EkIMMlxQDUcrZ9yoyJvTNjcVQ==", + "node_modules/@types/fs-extra": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.5.tgz", + "integrity": "sha512-wr3t7wIW1c0A2BIJtdVp4EflriVaVVAsCAIHVzzh8B+GiFv9X1xeJjCs4upRXtzp7kQ6lP5xvskjoD4awJ1ZeA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/geoip-country": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/geoip-country/-/geoip-country-4.0.0.tgz", + "integrity": "sha512-RngLyEh1cMcH/fphQa4+AiMJX+t0/kD/CijkRCgZzQWwFE5ZnSP/WxVhcMAHfTY7fNgZvWtkgeKBc96dsEss9Q==", "dev": true }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "node_modules/@types/geojson": { + "version": "7946.0.7", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==", "dev": true }, - "babel-preset-jest": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-22.4.4.tgz", - "integrity": "sha512-+dxMtOFwnSYWfum0NaEc0O03oSdwBsjx4tMSChRDPGwu/4wSY6Q6ANW3wkjKpJzzguaovRs/DODcT4hbSN8yiA==", + "node_modules/@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^22.4.4", - "babel-plugin-syntax-object-rest-spread": "^6.13.0" + "dependencies": { + "@types/node": "*" } }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "node_modules/@types/history": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==", + "dev": true + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", "dev": true, - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - }, "dependencies": { - "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", - "dev": true - } + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" } }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, + "node_modules/@types/html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==", + "dev": true + }, + "node_modules/@types/http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.4.tgz", + "integrity": "sha512-IrSHl2u6AWXduUaDLqYpt45tLVCtYv7o4Z0s1KghBCDgIIS9oW5K1H8mZG/A2CfeLdEa7rTd1ACOiHBc1EMT2Q==", + "dev": true, "dependencies": { - "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" - } + "@types/node": "*" } }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "dependencies": { + "@types/istanbul-lib-coverage": "*" } }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "node_modules/@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - }, "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" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "@types/istanbul-lib-report": "*" } }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - }, + "node_modules/@types/jest": { + "version": "26.0.19", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.19.tgz", + "integrity": "sha512-jqHoirTG61fee6v6rwbnEuKhpSKih0tuhqeFbCmMmErhtu3BYlOZaXWjffgOstMM4S/3iQD31lI5bGLTrs97yQ==", + "dev": true, "dependencies": { - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" - } + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" } }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + "node_modules/@types/js-cookie": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz", + "integrity": "sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw==", + "dev": true }, - "bail": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.4.tgz", - "integrity": "sha512-S8vuDB4w6YpRhICUDET3guPlQpaJl7od94tpZ0Fvnyp+MKW/HyDTcRDck+29C9g+d/qQHnddRH3+94kZdrW0Ww==" + "node_modules/@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "node_modules/@types/json-stable-stringify": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz", + "integrity": "sha512-q9Q6+eUEGwQkv4Sbst3J4PNgDOvpuVuKj79Hl/qnmBMEIPzB5QoFRUtjcgcg2xNUZyYUGXBk5wYIBKHt0A+Mxw==", + "dev": true }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "node_modules/@types/jsonwebtoken": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", + "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", + "dev": true, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } + "@types/node": "*" } }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "node_modules/@types/lodash": { + "version": "4.14.165", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz", + "integrity": "sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg==", "dev": true }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { - "safe-buffer": "5.1.2" + "node_modules/@types/loud-rejection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/loud-rejection/-/loud-rejection-2.0.0.tgz", + "integrity": "sha512-oTHISsIybJGoh3b3Ay/10csbAd2k0su7G7DGrE1QWciC+IdydPm0WMw1+Gr9YMYjPiJ5poB3g5Ev73IlLoavLw==", + "deprecated": "This is a stub types definition. loud-rejection provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "loud-rejection": "*" } }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "node_modules/@types/mime": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" + "node_modules/@types/minipass": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-2.2.0.tgz", + "integrity": "sha512-wuzZksN4w4kyfoOv/dlpov4NOunwutLA/q7uc00xU02ZyUY+aoM5PWIXEKBMnm0NHd4a+N71BMjq+x7+2Af1fg==", + "dev": true, + "dependencies": { + "@types/node": "*" } }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" - }, - "binary-search-tree": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz", - "integrity": "sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=", - "requires": { - "underscore": "~1.4.4" + "node_modules/@types/morgan": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.2.tgz", + "integrity": "sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ==", + "dev": true, + "dependencies": { + "@types/node": "*" } }, - "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "node_modules/@types/nedb": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/nedb/-/nedb-1.8.11.tgz", + "integrity": "sha512-qHQRLZ0e6l/XK/2Qb2v5N1ujmdttYkUvnRI4nPIifMy6vYwoAnER10xhX13isWjjQtNsrjNLinZgDDguzPmEKw==", + "dev": true, + "dependencies": { + "@types/node": "*" } }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "requires": { - "inherits": "~2.0.0" - } + "node_modules/@types/node": { + "version": "12.19.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.9.tgz", + "integrity": "sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==", + "dev": true }, - "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==" + "node_modules/@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "node_modules/@types/overlayscrollbars": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@types/overlayscrollbars/-/overlayscrollbars-1.12.0.tgz", + "integrity": "sha512-h/pScHNKi4mb+TrJGDon8Yb06ujFG0mSg12wIO0sWMUF3dQIe2ExRRdNRviaNt9IjxIiOfnRr7FsQAdHwK4sMg==", "dev": true }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, + "node_modules/@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 + }, + "node_modules/@types/passport": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.4.tgz", + "integrity": "sha512-h5OfAbfBBYSzjeU0GTuuqYEk9McTgWeGQql9g3gUw2/NNCfD7VgExVRYJVVeU13Twn202Mvk9BT0bUrl30sEgA==", + "dev": true, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "@types/express": "*" } }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "node_modules/@types/passport-jwt": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.3.tgz", + "integrity": "sha512-RlOCXiTitE8kazj9jZc6/BfGCSqnv2w/eYPDm3+3iNsquHn7ratu7oIUskZx9ZtnwMdpvdpy+Z/QYClocH5NvQ==", "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - }, "dependencies": { - "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 - } + "@types/express": "*", + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" } }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "node_modules/@types/passport-strategy": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", + "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "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 - }, - "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" - } - } + "@types/express": "*", + "@types/passport": "*" } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } + "node_modules/@types/prettier": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.5.tgz", + "integrity": "sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ==", + "dev": true }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } + "node_modules/@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "node_modules/@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", "dev": true }, - "browser-process-hrtime": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "node_modules/@types/qs": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", "dev": true }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "node_modules/@types/react": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", + "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", "dev": true, - "requires": { - "resolve": "1.1.7" - }, "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } + "@types/prop-types": "*", + "csstype": "^3.0.2" } }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "node_modules/@types/react-dnd-multi-backend": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dnd-multi-backend/-/react-dnd-multi-backend-6.0.0.tgz", + "integrity": "sha512-QSU2mnwwNIMKCH6LqarZGgJo918O9I9bCCSnCZfNRcCn2Fz9R6XYhldUes9b3cxwpG08QEz0zHYOytTM8NizQA==", "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "dependencies": { + "@types/react": "*", + "react-dnd": "^10.0.2", + "react-dnd-touch-backend": "^10.0.2" } }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "node_modules/@types/react-dnd-multi-backend/node_modules/dnd-core": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-10.0.2.tgz", + "integrity": "sha512-PrxEjxF0+6Y1n1n1Z9hSWZ1tvnDXv9syL+BccV1r1RC08uWNsyetf8AnWmUF3NgYPwy0HKQJwTqGkZK+1NlaFA==", "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "dependencies": { + "@react-dnd/asap": "^4.0.0", + "@react-dnd/invariant": "^2.0.0", + "redux": "^4.0.4" } }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "node_modules/@types/react-dnd-multi-backend/node_modules/react-dnd": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-10.0.2.tgz", + "integrity": "sha512-SC2Ymvntynhoqtf5zaFhZscm9xenCoMofilxPdlwUlaelAzmbl9fw82C4ZJ//+lNm3kWAKXjGDZg2/aWjKEAtg==", "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "dependencies": { + "@react-dnd/shallowequal": "^2.0.0", + "@types/hoist-non-react-statics": "^3.3.1", + "dnd-core": "^10.0.2", + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "react": ">= 16.8", + "react-dom": ">= 16.8" } }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "node_modules/@types/react-dnd-multi-backend/node_modules/react-dnd-touch-backend": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-10.0.2.tgz", + "integrity": "sha512-+lW/Ern0dKyHToD0oP+Wc/ZD6l7qJazosLqbjzL7OnPlig6WxdlrHkJylOLkeAdZj41fIJJ551Lb57pIL0CcPw==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" + "dependencies": { + "@react-dnd/invariant": "^2.0.0", + "dnd-core": "^10.0.2" } }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "node_modules/@types/react-dom": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz", + "integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==", "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "dependencies": { + "@types/react": "*" } }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "node_modules/@types/react-measure": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.6.tgz", + "integrity": "sha512-FxAwgDVKvxm4SPXu24x9cwzsty8x33UueazHcpxM1UWZlGJI57yIHM2djE3xUJhYVxuzNzi4E8UL3kmCkdh+4A==", "dev": true, - "requires": { - "pako": "~1.0.5" + "dependencies": { + "@types/react": "*" } }, - "browserslist": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.3.tgz", - "integrity": "sha512-jWvmhqYpx+9EZm/FxcZSbUZyDEvDTLDi3nSAKbzEkyWvtI0mNSmUosey+5awDW1RUlrgXbQb5A6qY1xQH9U6MQ==", - "requires": { - "caniuse-lite": "^1.0.30001010", - "electron-to-chromium": "^1.3.306", - "node-releases": "^1.1.40" - }, + "node_modules/@types/react-router": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz", + "integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==", + "dev": true, "dependencies": { - "caniuse-lite": { - "version": "1.0.30001011", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001011.tgz", - "integrity": "sha512-h+Eqyn/YA6o6ZTqpS86PyRmNWOs1r54EBDcd2NTwwfsXQ8re1B38SnB+p2RKF8OUsyEIjeDU8XGec1RGO/wYCg==" - }, - "electron-to-chromium": { - "version": "1.3.311", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.311.tgz", - "integrity": "sha512-7GH6RKCzziLzJ9ejmbiBEdzHZsc6C3eRpav14dmRfTWMpNgMqpP1ukw/FU/Le2fR+ep642naq7a23xNdmh2s+A==" - } + "@types/history": "*", + "@types/react": "*" } }, - "bser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", + "node_modules/@types/react-router-dom": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.6.tgz", + "integrity": "sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g==", "dev": true, - "requires": { - "node-int64": "^0.4.0" + "dependencies": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" } }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "node_modules/@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "dependencies": { + "@types/react": "*" } }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" + "node_modules/@types/react-window": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.2.tgz", + "integrity": "sha512-gP1xam68Wc4ZTAee++zx6pTdDAH08rAkQrWm4B4F/y6hhmlT9Mgx2q8lTCXnrPHXsr15XjRN9+K2DLKcz44qEQ==", + "dev": true, + "dependencies": { + "@types/react": "*" } }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + "node_modules/@types/serve-static": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz", + "integrity": "sha512-MoJhSQreaVoL+/hurAZzIm8wafFR6ajiTM1m4A0kv6AGeVBl4r4pOV8bGFrjjq1sGxDTnCoF8i22o0/aE5XCyA==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "node_modules/@types/spdy": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@types/spdy/-/spdy-3.4.4.tgz", + "integrity": "sha512-N9LBlbVRRYq6HgYpPkqQc3a9HJ/iEtVZToW6xlTtJiMhmRJ7jJdV7TaZQJw/Ve/1ePUsQiCTDc4JMuzzag94GA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "node_modules/@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", "dev": true }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", "dev": true }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", - "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - }, + "node_modules/@types/superagent": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.10.tgz", + "integrity": "sha512-xAgkb2CMWUMCyVc/3+7iQfOEBE75NvuZeezvmixbUw3nmENf2tCnQkW5yQLTYqvXUQ+R6EXxdqKKbal2zM5V/g==", + "dev": true, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } + "@types/cookiejar": "*", + "@types/node": "*" } }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - }, + "node_modules/@types/supertest": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.10.tgz", + "integrity": "sha512-Xt8TbEyZTnD5Xulw95GLMOkmjGICrOQyJ2jqgkSjAUR3mm7pAIzSR0NFBaMcwlzVvlpCjNwbATcWWwjNiZiFrQ==", + "dev": true, "dependencies": { - "bluebird": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", - "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==" - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } + "@types/superagent": "*" } }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, + "node_modules/@types/tar": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.4.tgz", + "integrity": "sha512-0Xv+xcmkTsOZdIF4yCnd7RkOOyfyqPaqJ7RZFKnwdxfDbkN3eAAE9sHl8zJFqBz4VhxolW9EErbjR1oyH7jK2A==", + "dev": true, "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } + "@types/minipass": "*", + "@types/node": "*" } }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", - "dev": true + "node_modules/@types/yargs": { + "version": "15.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.11.tgz", + "integrity": "sha512-jfcNBxHFYJ4nPIacsi3woz1+kvUO6s1CyeEhtnDHBjHUMNj5UlW2GynmnSgiJJEdNg9yW5C8lfoNRZrHGv5EqA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.1.tgz", + "integrity": "sha512-QRLDSvIPeI1pz5tVuurD+cStNR4sle4avtHhxA+2uyixWGFjKzJ+EaFVRW6dA/jOgjV5DTAjOxboQkRDE8cRlQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "4.9.1", + "@typescript-eslint/scope-manager": "4.9.1", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^4.0.0", + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" + "node_modules/@typescript-eslint/experimental-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.1.tgz", + "integrity": "sha512-c3k/xJqk0exLFs+cWSJxIjqLYwdHCuLWhnpnikmPQD2+NGAx9KjLYlBDcSI81EArh9FDYSL6dslAUSwILeWOxg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.9.1", + "@typescript-eslint/types": "4.9.1", + "@typescript-eslint/typescript-estree": "4.9.1", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.9.1.tgz", + "integrity": "sha512-Gv2VpqiomvQ2v4UL+dXlQcZ8zCX4eTkoIW+1aGVWT6yTO+6jbxsw7yQl2z2pPl/4B9qa5JXeIbhJpONKjXIy3g==", + "dev": true, "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + "@typescript-eslint/scope-manager": "4.9.1", + "@typescript-eslint/types": "4.9.1", + "@typescript-eslint/typescript-estree": "4.9.1", + "debug": "^4.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "caniuse-lite": { - "version": "1.0.30000963", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000963.tgz", - "integrity": "sha512-n4HUiullc7Lw0LyzpeLa2ffP8KxFBGdxqD/8G3bSL6oB758hZ2UE2CVK+tQN958tJIi0/tfpjAc67aAtoHgnrQ==" - }, - "capture-exit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-1.2.0.tgz", - "integrity": "sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28=", + "node_modules/@typescript-eslint/scope-manager": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz", + "integrity": "sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==", "dev": true, - "requires": { - "rsvp": "^3.3.3" + "dependencies": { + "@typescript-eslint/types": "4.9.1", + "@typescript-eslint/visitor-keys": "4.9.1" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "dev": true + "node_modules/@typescript-eslint/types": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.1.tgz", + "integrity": "sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==", + "dev": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "case-sensitive-paths-webpack-plugin": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.1.2.tgz", - "integrity": "sha512-oEZgAFfEvKtjSRCu6VgYkuGxwrWXMnQzyBmlLPP7r6PWQVtHxP5Z5N6XsuJvtoVax78am/r7lr46bwo3IVEBOg==" + "node_modules/@typescript-eslint/typescript-estree": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz", + "integrity": "sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.9.1", + "@typescript-eslint/visitor-keys": "4.9.1", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "catharsis": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", - "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz", + "integrity": "sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==", "dev": true, - "requires": { - "lodash": "^4.17.14" + "dependencies": { + "@typescript-eslint/types": "4.9.1", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "node_modules/@vercel/ncc": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.25.1.tgz", + "integrity": "sha512-dGecC5+1wLof1MQpey4+6i2KZv4Sfs6WfXkl9KfO32GED4ZPiKxRfvtGPjbjZv0IbqMl6CxtcV1RotXYfd5SSA==", + "dev": true, + "bin": { + "ncc": "dist/ncc/cli.js" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "node_modules/@webassemblyjs/ast": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.1.tgz", + "integrity": "sha512-uMu1nCWn2Wxyy126LlGqRVlhdTOsO/bsBRI4dNq3+6SiSuRKRQX6ejjKgh82LoGAPSq72lDUiQ4FWVaf0PecYw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-module-context": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/wast-parser": "1.9.1" } }, - "character-entities": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.3.tgz", - "integrity": "sha512-yB4oYSAa9yLcGyTbB4ItFwHw43QHdH129IJ5R+WvxOkWlyFnR5FAaBNnUq4mcxsTVZGh28bHoeTHMKXH1wZf3w==" + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.1.tgz", + "integrity": "sha512-5VEKu024RySmLKTTBl9q1eO/2K5jk9ZS+2HXDBLA9s9p5IjkaXxWiDb/+b7wSQp6FRdLaH1IVGIfOex58Na2pg==", + "dev": true }, - "character-entities-legacy": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.3.tgz", - "integrity": "sha512-YAxUpPoPwxYFsslbdKkhrGnXAtXoHNgYjlBM3WMXkWGTl5RsY3QmOyhwAgL8Nxm9l5LBThXGawxKPn68y6/fww==" + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.1.tgz", + "integrity": "sha512-y1lGmfm38djrScwpeL37rRR9f1D6sM8RhMpvM7CYLzOlHVboouZokXK/G88BpzW0NQBSvCCOnW5BFhten4FPfA==", + "dev": true }, - "character-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", - "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", - "requires": { - "is-regex": "^1.0.3" - } + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.1.tgz", + "integrity": "sha512-uS6VSgieHbk/m4GSkMU5cqe/5TekdCzQso4revCIEQ3vpGZgqSSExi4jWpTWwDpAHOIAb1Jfrs0gUB9AA4n71w==", + "dev": true }, - "character-reference-invalid": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.3.tgz", - "integrity": "sha512-VOq6PRzQBam/8Jm6XBGk2fNEnHXAdGd6go0rtd4weAGECBamHDwwCQSOT12TACIYUZegUXnV6xBXqUssijtxIg==" + "node_modules/@webassemblyjs/helper-code-frame": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.1.tgz", + "integrity": "sha512-ZQ2ZT6Evk4DPIfD+92AraGYaFIqGm4U20e7FpXwl7WUo2Pn1mZ1v8VGH8i+Y++IQpxPbQo/UyG0Khs7eInskzA==", + "dev": true, + "dependencies": { + "@webassemblyjs/wast-printer": "1.9.1" + } }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "node_modules/@webassemblyjs/helper-fsm": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.1.tgz", + "integrity": "sha512-J32HGpveEqqcKFS0YbgicB0zAlpfIxJa5MjxDxhu3i5ltPcVfY5EPvKQ1suRguFPehxiUs+/hfkwPEXom/l0lw==", "dev": true }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "node_modules/@webassemblyjs/helper-module-context": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.1.tgz", + "integrity": "sha512-IEH2cMmEQKt7fqelLWB5e/cMdZXf2rST1JIrzWmf4XBt3QTxGdnnLvV4DYoN8pJjOx0VYXsWg+yF16MmJtolZg==", "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, "dependencies": { - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "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-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "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 - } - } - }, - "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" - }, - "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" + "@webassemblyjs/ast": "1.9.1" } }, - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.1.tgz", + "integrity": "sha512-i2rGTBqFUcSXxyjt2K4vm/3kkHwyzG6o427iCjcIKjOqpWH8SEem+xe82jUk1iydJO250/CvE5o7hzNAMZf0dQ==", "dev": true }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.1.tgz", + "integrity": "sha512-FetqzjtXZr2d57IECK+aId3D0IcGweeM0CbAnJHkYJkcRTHP+YcMb7Wmc0j21h5UWBpwYGb9dSkK/93SRCTrGg==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "dependencies": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-buffer": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/wasm-gen": "1.9.1" } }, - "clarinet": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/clarinet/-/clarinet-0.12.3.tgz", - "integrity": "sha512-Vdv6MsBQWppWUCe/5EGa6JSYln0coSK98NsLAAbdqM3uTXmySZoGQXAMDgOAOlf5wjSIdeHVx9jhjkXOVDvoIA==" - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.1.tgz", + "integrity": "sha512-EvTG9M78zP1MmkBpUjGQHZc26DzPGZSLIPxYHCjQsBMo60Qy2W34qf8z0exRDtxBbRIoiKa5dFyWer/7r1aaSQ==", + "dev": true, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } + "@xtuc/ieee754": "^1.2.0" } }, - "classnames": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" - }, - "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", - "requires": { - "source-map": "~0.6.0" - }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.1.tgz", + "integrity": "sha512-Oc04ub0vFfLnF+2/+ki3AE+anmW4sv9uNBqb+79fgTaPv6xJsOT0dhphNfL3FrME84CbX/D1T9XT8tjFo0IIiw==", + "dev": true, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + "@xtuc/long": "4.2.2" } }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "node_modules/@webassemblyjs/utf8": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.1.tgz", + "integrity": "sha512-llkYtppagjCodFjo0alWOUhAkfOiQPQDIc5oA6C9sFAXz7vC9QhZf/f8ijQIX+A9ToM3c9Pq85X0EX7nx9gVhg==", "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=", + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.1.tgz", + "integrity": "sha512-S2IaD6+x9B2Xi8BCT0eGsrXXd8UxAh2LVJpg1ZMtHXnrDcsTtIX2bDjHi40Hio6Lc62dWHmKdvksI+MClCYbbw==", "dev": true, - "requires": { - "restore-cursor": "^2.0.0" + "dependencies": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-buffer": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/helper-wasm-section": "1.9.1", + "@webassemblyjs/wasm-gen": "1.9.1", + "@webassemblyjs/wasm-opt": "1.9.1", + "@webassemblyjs/wasm-parser": "1.9.1", + "@webassemblyjs/wast-printer": "1.9.1" } }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.1.tgz", + "integrity": "sha512-bqWI0S4lBQsEN5FTZ35vYzfKUJvtjNnBobB1agCALH30xNk1LToZ7Z8eiaR/Z5iVECTlBndoRQV3F6mbEqE/fg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/ieee754": "1.9.1", + "@webassemblyjs/leb128": "1.9.1", + "@webassemblyjs/utf8": "1.9.1" + } }, - "clipboard": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.1.tgz", + "integrity": "sha512-gSf7I7YWVXZ5c6XqTEqkZjVs8K1kc1k57vsB6KBQscSagDNbAdxt6MwuJoMjsE1yWY1tsuL+pga268A6u+Fdkg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-buffer": "1.9.1", + "@webassemblyjs/wasm-gen": "1.9.1", + "@webassemblyjs/wasm-parser": "1.9.1" } }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.1.tgz", + "integrity": "sha512-ImM4N2T1MEIond0MyE3rXvStVxEmivQrDKf/ggfh5pP6EHu3lL/YTAoSrR7shrbKNPpeKpGesW1LIK/L4kqduw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-api-error": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/ieee754": "1.9.1", + "@webassemblyjs/leb128": "1.9.1", + "@webassemblyjs/utf8": "1.9.1" } }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + "node_modules/@webassemblyjs/wast-parser": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.1.tgz", + "integrity": "sha512-2xVxejXSvj3ls/o2TR/zI6p28qsGupjHhnHL6URULQRcXmryn3w7G83jQMcT7PHqUfyle65fZtWLukfdLdE7qw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/floating-point-hex-parser": "1.9.1", + "@webassemblyjs/helper-api-error": "1.9.1", + "@webassemblyjs/helper-code-frame": "1.9.1", + "@webassemblyjs/helper-fsm": "1.9.1", + "@xtuc/long": "4.2.2" + } }, - "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==", - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.1.tgz", + "integrity": "sha512-tDV8V15wm7mmbAH6XvQRU1X+oPGmeOzYsd6h7hlRLz6QpV4Ec/KKxM8OpLtFmQPLCreGxTp+HuxtH4pRIZyL9w==", + "dev": true, "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/wast-parser": "1.9.1", + "@xtuc/long": "4.2.2" } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "node_modules/@xobotyi/scrollbar-width": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", + "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==", "dev": true }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "node_modules/@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 }, - "collapse-white-space": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.5.tgz", - "integrity": "sha512-703bOOmytCYAX9cXYqoikYIx6twmFCXsnzRQheBcTG3nzKYBR4P/+wkYeH+Mvj7qUz8zZDtdyzbxfnEi/kYzRQ==" + "node_modules/@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 }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true }, - "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==", - "requires": { - "color-name": "1.1.3" + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "requires": { - "delayed-stream": "~1.0.0" + "node_modules/acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "compressible": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", - "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", - "requires": { - "mime-db": ">= 1.40.0 < 2" + "node_modules/address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "dev": true, + "engines": { + "node": ">= 0.12.0" } }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "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" - }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "node_modules/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, - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } + "peerDependencies": { + "ajv": "^6.9.1" } }, - "confusing-browser-globals": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz", - "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==", + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", "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 + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true, + "engines": { + "node": ">=0.4.2" + } }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, - "requires": { - "date-now": "^0.1.4" + "engines": { + "node": ">=6" } }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "node_modules/ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "dependencies": { + "type-fest": "^0.11.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "constantinople": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", - "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", - "requires": { - "@types/babel-types": "^7.0.0", - "@types/babylon": "^6.16.2", - "babel-types": "^6.26.0", - "babylon": "^6.18.0" + "node_modules/ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" } }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true + "node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true + "node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" + "node_modules/anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "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==" + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "node_modules/are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, - "requires": { - "safe-buffer": "~5.1.1" + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-parser": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz", - "integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==", - "requires": { - "cookie": "0.3.1", - "cookie-signature": "1.0.6" + "node_modules/are-we-there-yet/node_modules/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, + "dependencies": { + "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" } }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "node_modules/are-we-there-yet/node_modules/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 }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "node_modules/are-we-there-yet/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, - "core-js-compat": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.2.tgz", - "integrity": "sha512-W0Aj+LM3EAxxjD0Kp2o4be8UlnxIZHNupBv2znqrheR4aY2nOn91794k/xoSp+SxqqriiZpTsSwBtZr60cbkwQ==", - "requires": { - "browserslist": "^4.7.3", - "semver": "^6.3.0" - }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } + "sprintf-js": "~1.0.2" } }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "node_modules/argparse/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true }, - "cosmiconfig": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.4.3", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "os-homedir": "^1.0.1", - "parse-json": "^2.2.0", - "require-from-string": "^1.1.0" + "node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" } }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" + "engines": { + "node": ">=0.10.0" } }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" + "engines": { + "node": ">=0.10.0" } }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "engines": { + "node": ">=0.10.0" } }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, + "node_modules/array-find": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz", + "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", + "dev": true + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "engines": { + "node": ">=0.10.0" } }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", + "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "get-intrinsic": "^1.0.1", + "is-string": "^1.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "node_modules/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, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "engines": { + "node": ">=8" } }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", - "dev": true + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "css-loader": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.2.0.tgz", - "integrity": "sha512-QTF3Ud5H7DaZotgdcJjGMvyDj5F3Pn1j/sC6VBEOVp94cbwqyIBdcs/quzj4MC1BKQSrTpQznegH/5giYbhnCQ==", - "requires": { - "camelcase": "^5.3.1", - "cssesc": "^3.0.0", - "icss-utils": "^4.1.1", - "loader-utils": "^1.2.3", - "normalize-path": "^3.0.0", - "postcss": "^7.0.17", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.2", - "postcss-modules-scope": "^2.1.0", - "postcss-modules-values": "^3.0.0", - "postcss-value-parser": "^4.0.0", - "schema-utils": "^2.0.0" - }, + "node_modules/array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, "dependencies": { - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "postcss": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.23.tgz", - "integrity": "sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-value-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", - "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "css-modules-loader-core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", - "integrity": "sha1-WQhmgpShvs0mGuCkziGwtVHyHRY=", - "requires": { - "icss-replace-symbols": "1.1.0", - "postcss": "6.0.1", - "postcss-modules-extract-imports": "1.1.0", - "postcss-modules-local-by-default": "1.2.0", - "postcss-modules-scope": "1.1.0", - "postcss-modules-values": "1.3.0" - }, + "node_modules/array.prototype.flatmap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", + "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" - }, - "postcss": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.1.tgz", - "integrity": "sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=", - "requires": { - "chalk": "^1.1.3", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "postcss-modules-extract-imports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", - "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", - "requires": { - "postcss": "^6.0.1" - } - }, - "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "requires": { - "has-flag": "^1.0.0" - } - } + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, + "engines": { + "node": ">=8" } }, - "css-selector-tokenizer": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", - "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", - "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" - }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, "dependencies": { - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=" - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "requires": { - "jsesc": "~0.5.0" - } - } + "safer-buffer": "~2.1.0" } }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } }, - "cssom": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", "dev": true }, - "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", "dev": true, - "requires": { - "cssom": "0.3.x" + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" } }, - "csstype": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz", - "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==" - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "requires": { - "array-find-index": "^1.0.1" + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "engines": { + "node": ">=0.8" } }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" - }, - "d3": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz", - "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=" - }, - "damerau-levenshtein": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz", - "integrity": "sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==", + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", "dev": true }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "dependencies": { + "inherits": "2.0.1" } }, - "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true, - "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" - }, - "dependencies": { - "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } + "engines": { + "node": ">=0.10.0" } }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", "dev": true }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" + "node_modules/astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true, + "engines": { + "node": ">=4" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dependencies": { + "lodash": "^4.17.14" + } }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + "node_modules/async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true, + "engines": { + "node": "*" + } }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "deepmerge": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.3.2.tgz", - "integrity": "sha1-FmNpFinU2/42T6EqKk8KqGqjoFA=" - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "node_modules/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, - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - }, - "dependencies": { - "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" - } - }, - "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" - } - }, - "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" - } - } + "engines": { + "node": ">= 4.0.0" } }, - "default-require-extensions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", - "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true, - "requires": { - "strip-bom": "^2.0.0" + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" } }, - "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==", - "requires": { - "object-keys": "^1.0.12" + "node_modules/attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==", + "dev": true, + "engines": { + "node": ">=4" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, + "node_modules/autoprefixer": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.1.0.tgz", + "integrity": "sha512-0/lBNwN+ZUnb5su18NZo5MBIjDaq6boQKZcxwy86Gip/CmXA2zZqUoFQLCNAGI5P25ZWSP2RWdhDJ8osfKEjoQ==", + "dev": true, "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } + "browserslist": "^4.15.0", + "caniuse-lite": "^1.0.30001165", + "colorette": "^1.2.1", + "fraction.js": "^4.0.12", + "normalize-range": "^0.1.2", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "node_modules/available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, "dependencies": { - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - } + "array-filter": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true, + "engines": { + "node": "*" + } }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "node_modules/axe-core": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.1.tgz", + "integrity": "sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "engines": { + "node": ">=4" } }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "node_modules/axios": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", + "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.10.0" + } }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", "dev": true }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "node_modules/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": { - "repeating": "^2.0.0" + "dependencies": { + "@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" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "eslint": ">= 4.12.1" } }, - "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", - "dev": true - }, - "detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + "node_modules/babel-eslint/node_modules/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, + "engines": { + "node": ">=4" + } }, - "detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "node_modules/babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", "dev": true, - "requires": { - "address": "^1.0.1", - "debug": "^2.6.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 - } + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", - "requires": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" - }, + "node_modules/babel-loader": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", + "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "dev": true, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" } }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/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": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "dependencies": { + "object.assign": "^4.1.0" } }, - "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "node_modules/babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", "dev": true, - "requires": { - "arrify": "^1.0.1", - "path-type": "^3.0.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, "dependencies": { - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": ">= 10.14.2" } }, - "disposables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/disposables/-/disposables-1.0.2.tgz", - "integrity": "sha1-NsamdEdfVaLWkTVnpgFETkh7S24=" - }, - "dnd-core": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz", - "integrity": "sha1-ErrWbVh0LG5ffPKUP7aFlED4CcQ=", - "requires": { - "asap": "^2.0.6", - "invariant": "^2.0.0", - "lodash": "^4.2.0", - "redux": "^3.7.1" - } - }, - "dns-equal": { + "node_modules/babel-preset-current-node-syntax": { "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": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.0.tgz", + "integrity": "sha512-mGkvkpocWJes1CmMKtgGUwCeeq0pOhALyymozzDWYomHTbDLwueDYG6p4TK1YOeYHCzBzYPsWkgTto10JubI1Q==", "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "node_modules/babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "dependencies": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "doctypes": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "requires": { - "utila": "~0.4" + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "dom-css": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/dom-css/-/dom-css-2.1.0.tgz", - "integrity": "sha1-/bwtWgFdCj4YcuEUcrvQ57nmogI=", - "requires": { - "add-px-to-style": "1.0.0", - "prefix-style": "2.0.1", - "to-camel-case": "1.0.0" + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "dom-helpers": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz", - "integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==", - "requires": { - "@babel/runtime": "^7.6.3", - "csstype": "^2.6.7" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz", - "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==", - "requires": { - "regenerator-runtime": "^0.13.2" - } + "node_modules/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, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } - } + ] }, - "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" } }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "node_modules/basic-auth/node_modules/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 }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, - "requires": { - "webidl-conversions": "^4.0.2" + "dependencies": { + "tweetnacl": "^0.14.3" } }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" + "node_modules/bencode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bencode/-/bencode-2.0.1.tgz", + "integrity": "sha512-2uhEl8FdjSBUyb69qDTgOEeeqDTa+n3yMQzLW0cOzNf1Ow5bwcg3idf+qsWisIKRH8Bk8oC7UXL8irRcPA8ZEQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.1" } }, - "domready": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/domready/-/domready-1.0.8.tgz", - "integrity": "sha1-kfJS5Ze2Wvd+dFriTdAYXV4m1Yw=" + "node_modules/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, + "engines": { + "node": "*" + } }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "node_modules/binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "node_modules/binary-search-tree": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz", + "integrity": "sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=", "dev": true, - "requires": { - "is-obj": "^1.0.0" + "dependencies": { + "underscore": "~1.4.4" } }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", - "dev": true + "node_modules/block-stream2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.0.0.tgz", + "integrity": "sha512-1oI+RHHUEo64xomy1ozLgVJetFlHkIfQfJzTBQrj6xWnEMEPooeo2fZoqFjp0yzfHMBrgxwgh70tKp6T17+i3g==", + "dev": true, + "dependencies": { + "readable-stream": "^3.4.0" + } }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "node_modules/bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", "dev": true }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, "dependencies": { - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - } + "ms": "2.0.0" } }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" + "node_modules/body-parser/node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" } }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "node_modules/body-parser/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true }, - "electron-to-chromium": { - "version": "1.3.127", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.127.tgz", - "integrity": "sha512-1o25iFRf/dbgauTWalEzmD1EmRN3a2CzP/K7UVpYLEBduk96LF0FyUdCcf4Ry2mAWJ1VxyblFjC93q6qlLwA2A==" + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true }, - "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "node_modules/bonjour/node_modules/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 }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } + "node_modules/bowser": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz", + "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==", + "dev": true }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "^1.4.0" + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "enhanced-resolve": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", + "node_modules/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": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.2.0", - "tapable": "^0.1.8" - }, "dependencies": { - "tapable": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", - "dev": true - } - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "requires": { - "prr": "~1.0.1" + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" } }, - "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==", - "requires": { - "is-arrayish": "^0.2.1" - } + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true }, - "es-to-primitive": { + "node_modules/browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "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=" - }, - "escodegen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", - "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, - "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^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" - } - } + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" } }, - "eslint-config-airbnb": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-17.1.1.tgz", - "integrity": "sha512-xCu//8a/aWqagKljt+1/qAM62BYZeNq04HmdevG5yUGWpja0I/xhqd6GdLRch5oetEGFiJAnvtGuTEAese53Qg==", + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, - "requires": { - "eslint-config-airbnb-base": "^13.2.0", - "object.assign": "^4.1.0", - "object.entries": "^1.1.0" + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "eslint-config-airbnb-base": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.2.0.tgz", - "integrity": "sha512-1mg/7eoB4AUeB0X1c/ho4vb2gYkNH8Trr/EgCT/aGmKhhG+F6vF5s8+iRBlWAzFIAphxIdp3YfEKgEl0f9Xg+w==", + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", "dev": true, - "requires": { - "confusing-browser-globals": "^1.0.5", - "object.assign": "^4.1.0", - "object.entries": "^1.1.0" + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" } }, - "eslint-config-prettier": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.3.0.tgz", - "integrity": "sha512-sZwhSTHVVz78+kYD3t5pCWSYEdVSBR0PXnwjDRsUs8ytIrK8PLXw+6FKp8r3Z7rx4ZszdetWlXYKOHoUrrwPlA==", + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", "dev": true, - "requires": { - "get-stdin": "^6.0.0" + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "dependencies": { + "pako": "~1.0.5" } }, - "eslint-config-react-app": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-2.1.0.tgz", - "integrity": "sha512-8QZrKWuHVC57Fmu+SsKAVxnI9LycZl7NFQ4H9L+oeISuCXhYdXqsOOIVSjQFW6JF5MXZLFE+21Syhd7mF1IRZQ==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "node_modules/browserslist": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.15.0.tgz", + "integrity": "sha512-IJ1iysdMkGmjjYeRlDU8PQejVwxvVO5QOfXH7ylW31GO6LwNRSmm/SgRXtNsEXqMLl2e+2H5eEJ7sfynF8TCaQ==", "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^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" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "caniuse-lite": "^1.0.30001164", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.612", + "escalade": "^3.1.1", + "node-releases": "^1.1.67" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" } }, - "eslint-import-resolver-webpack": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.11.1.tgz", - "integrity": "sha512-eK3zR7xVQR/MaoBWwGuD+CULYVuqe5QFlDukman71aI6IboCGzggDUohHNfu1ZeBnbHcUHJc0ywWoXUBNB6qdg==", + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, - "requires": { - "array-find": "^1.0.0", - "debug": "^2.6.8", - "enhanced-resolve": "~0.9.0", - "find-root": "^1.1.0", - "has": "^1.0.1", - "interpret": "^1.0.0", - "lodash": "^4.17.4", - "node-libs-browser": "^1.0.0 || ^2.0.0", - "resolve": "^1.10.0", - "semver": "^5.3.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 - } + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" } }, - "eslint-loader": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.2.1.tgz", - "integrity": "sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==", + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "requires": { - "loader-fs-cache": "^1.0.0", - "loader-utils": "^1.0.2", - "object-assign": "^4.0.1", - "object-hash": "^1.1.4", - "rimraf": "^2.6.1" + "dependencies": { + "node-int64": "^0.4.0" } }, - "eslint-module-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz", - "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==", + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^2.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" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - } + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, - "eslint-plugin-flowtype": { - "version": "2.46.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.46.1.tgz", - "integrity": "sha512-GJzxW7QwiIiW0ZA/+nY+N5TuK3es4Uei0D4xuy14dLZBYEFhM6e7c0J1u4+/iwfPqFtkr5a0oSApnSKF4U6KHw==", + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "dev": true + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true, - "requires": { - "lodash": "^4.15.0" + "engines": { + "node": ">= 0.8" } }, - "eslint-plugin-import": { - "version": "2.18.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", - "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "node_modules/cacache": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz", + "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==", "dev": true, - "requires": { - "array-includes": "^3.0.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.0", - "read-pkg-up": "^2.0.0", - "resolve": "^1.11.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" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "resolve": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.2.tgz", - "integrity": "sha512-cAVTI2VLHWYsGOirfeYVVQ7ZDejtQ9fp4YhYckWDEkFfqbVjaT11iM8k6xSAfGFMM+gDpZjMnFssPu8we+mqFw==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" } }, - "eslint-plugin-jsx-a11y": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz", - "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==", + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, - "requires": { - "@babel/runtime": "^7.4.5", - "aria-query": "^3.0.0", - "array-includes": "^3.0.3", - "ast-types-flow": "^0.0.7", - "axobject-query": "^2.0.2", - "damerau-levenshtein": "^1.0.4", - "emoji-regex": "^7.0.2", - "has": "^1.0.3", - "jsx-ast-utils": "^2.2.1" - }, "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, - "jsx-ast-utils": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz", - "integrity": "sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "object.assign": "^4.1.0" - } - }, - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", - "dev": true - } + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "eslint-plugin-react": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.16.0.tgz", - "integrity": "sha512-GacBAATewhhptbK3/vTP09CbFrgUJmBSaaRcWdbQLFvUZy9yVcQxigBNHGPU/KE2AyHpzj3AWXpxoMTsIDiHug==", + "node_modules/call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", "dev": true, - "requires": { - "array-includes": "^3.0.3", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.2.1", - "object.entries": "^1.1.0", - "object.fromentries": "^2.0.0", - "object.values": "^1.1.0", - "prop-types": "^15.7.2", - "resolve": "^1.12.0" + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.2.tgz", - "integrity": "sha512-cAVTI2VLHWYsGOirfeYVVQ7ZDejtQ9fp4YhYckWDEkFfqbVjaT11iM8k6xSAfGFMM+gDpZjMnFssPu8we+mqFw==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" + "node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true, + "engines": { + "node": ">=4" + } }, - "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "node_modules/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": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - }, "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - } + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" } }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true, - "requires": { - "estraverse": "^4.0.0" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "requires": { - "estraverse": "^4.1.0" + "node_modules/camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "dependencies": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", - "dev": true + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "events": { + "node_modules/caniuse-api": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", - "dev": true - }, - "eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dev": true, - "requires": { - "original": "^1.0.0" + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" } }, - "eventyoshi": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/eventyoshi/-/eventyoshi-0.2.1.tgz", - "integrity": "sha512-74HGaBn3Okqlv+mLFBRgqAgG5X8FvmHzrAZcJ7SG8nZZiLgRR2or0kJrkJ3GT5NncQDwHYh/sO7Cpzne+Ep2LA==" + "node_modules/caniuse-lite": { + "version": "1.0.30001165", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz", + "integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==", + "dev": true }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "node_modules/capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "dependencies": { + "rsvp": "^4.8.4" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "exec-sh": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", - "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==", "dev": true, - "requires": { - "merge": "^1.2.0" + "engines": { + "node": ">=4" } }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.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" - }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" + "engines": { + "node": ">=10" } }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "node_modules/chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, - "requires": { - "fill-range": "^2.1.0" + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.1.2" } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "node_modules/chokidar/node_modules/fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "expect": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-22.4.3.tgz", - "integrity": "sha512-XcNXEPehqn8b/jm8FYotdX0YrXn36qp4HWlrVT4ktwQas1l1LPxiVWncYnnL2eyMtKAmVIaG0XAp0QlrqJaxaA==", + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "jest-diff": "^22.4.3", - "jest-get-type": "^22.4.3", - "jest-matcher-utils": "^22.4.3", - "jest-message-util": "^22.4.3", - "jest-regex-util": "^22.4.3" + "engines": { + "node": ">=10" } }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "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.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, + "node_modules/chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, "dependencies": { - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - } + "tslib": "^1.9.0" + }, + "engines": { + "node": ">=6.0" } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "node_modules/chrome-trace-event/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, - "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "node_modules/cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "node_modules/clarinet": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/clarinet/-/clarinet-0.12.4.tgz", + "integrity": "sha512-Rx9Zw8KQkoPO3/O2yPRchCZm3cGubCQiRLmmFAlbkDKobUIPP3JYul+bKILR9DIv1gSVwPQSgF8JGGkXzX8Q0w==", "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "engines": { + "chrome": ">=16.0.912", + "firefox": ">=0.8.0", + "node": ">=0.3.6" } }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, - "requires": { - "is-extglob": "^1.0.0" + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-glob": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", - "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, - "requires": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.1.2", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.3", - "micromatch": "^3.1.10" - }, "dependencies": { - "arr-diff": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==", + "dev": true + }, + "node_modules/clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/clipboard": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz", + "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==", + "dev": true, + "dependencies": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "node_modules/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, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/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 + }, + "node_modules/cliui/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/coa/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/coa/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/coa/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/coa/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/coa/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, + "node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/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 + }, + "node_modules/color-string": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/compression/node_modules/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 + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz", + "integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ==", + "dev": true + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "node_modules/contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/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 + }, + "node_modules/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, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/convert-source-map/node_modules/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 + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", + "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", + "dev": true, + "dependencies": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "dev": true, + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/core-js-compat": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.1.tgz", + "integrity": "sha512-a16TLmy9NVD1rkjUGbwuyWkiDoN0FDpAwrfLONvHFQx0D9k7J9y0srwMT8QP/Z6HE3MIFaVynEeYwZwPX1o5RQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.15.0", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-js-pure": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.1.tgz", + "integrity": "sha512-Se+LaxqXlVXGvmexKGPvnUIYC1jwXu1H6Pkyb3uBM5d8/NELMYCHs/4/roD7721NxrTLyv7e5nXd5/QLBO+10g==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/create-torrent": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/create-torrent/-/create-torrent-4.4.2.tgz", + "integrity": "sha512-FRxgYty6AF00xrYKMtpQ14ZJlst+i7mmUhcN4do7TTjktEntqAzfriaOIV6xk27t9GLTtraFnaTxsGgnyFA2eA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "bencode": "^2.0.0", + "block-stream2": "^2.0.0", + "filestream": "^5.0.0", + "is-file": "^1.0.0", + "junk": "^3.1.0", + "minimist": "^1.1.0", + "multistream": "^4.0.0", + "once": "^1.3.0", + "piece-length": "^2.0.1", + "readable-stream": "^3.0.2", + "run-parallel": "^1.0.0", + "simple-sha1": "^3.0.0" + }, + "bin": { + "create-torrent": "bin/cmd.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "engines": { + "node": ">4" + } + }, + "node_modules/css-declaration-sorter/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-declaration-sorter/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-declaration-sorter/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-declaration-sorter/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/css-declaration-sorter/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/css-declaration-sorter/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/css-declaration-sorter/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/css-declaration-sorter/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/css-declaration-sorter/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-declaration-sorter/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/css-in-js-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", + "dev": true, + "dependencies": { + "hyphenate-style-name": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "node_modules/css-loader": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.0.1.tgz", + "integrity": "sha512-cXc2ti9V234cq7rJzFKhirb2L2iPy8ZjALeVJAozXYz9te3r4eqLSixNAbMDJSgJEQywqXzs8gonxaboeKqwiw==", + "dev": true, + "dependencies": { + "camelcase": "^6.2.0", + "cssesc": "^3.0.0", + "icss-utils": "^5.0.0", + "loader-utils": "^2.0.0", + "postcss": "^8.1.4", + "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.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.27.0 || ^5.0.0" + } + }, + "node_modules/css-loader/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/css-loader/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-1.1.5.tgz", + "integrity": "sha512-mXgaoFjNpIudZfxD49N1aPtLxfXGJt+BVPVjQ+H66I48b5n4wJtFpYfffVr7izK8W6fD01J7K0kUcP6HGjw90w==", + "dev": true, + "dependencies": { + "cacache": "^15.0.5", + "cssnano": "^4.1.10", + "find-cache-dir": "^3.3.1", + "jest-worker": "^26.3.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "webpack-sources": "^1.4.3" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "dependencies": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true + }, + "node_modules/css-tree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.2.tgz", + "integrity": "sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/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, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-preset-default/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-preset-default/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-preset-default/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/cssnano-preset-default/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/cssnano-preset-default/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/cssnano-preset-default/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/cssnano-util-raw-cache/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/cssnano/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/cssnano/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cssnano/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/cssnano/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "dev": true + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/d3-array": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", + "integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==", + "dev": true + }, + "node_modules/d3-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==", + "dev": true + }, + "node_modules/d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==", + "dev": true + }, + "node_modules/d3-interpolate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", + "dev": true, + "dependencies": { + "d3-color": "1 - 2" + } + }, + "node_modules/d3-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz", + "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==", + "dev": true + }, + "node_modules/d3-scale": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz", + "integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==", + "dev": true, + "dependencies": { + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "1 - 2", + "d3-time-format": "2 - 3" + } + }, + "node_modules/d3-selection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz", + "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA==", + "dev": true + }, + "node_modules/d3-shape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.0.0.tgz", + "integrity": "sha512-djpGlA779ua+rImicYyyjnOjeubyhql1Jyn1HK0bTyawuH76UQRWXd+pftr67H6Fa8hSwetkgb/0id3agKWykw==", + "dev": true, + "dependencies": { + "d3-path": "1 - 2" + } + }, + "node_modules/d3-time": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz", + "integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q==", + "dev": true + }, + "node_modules/d3-time-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", + "dev": true, + "dependencies": { + "d3-time": "1 - 2" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", + "dev": true + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + }, + "bin": { + "dateformat": "bin/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.2.tgz", + "integrity": "sha512-bWrj9HZWNXJ/RUkWmBIp67JawNrPGz0il43IGWU84dazEYbNFQ52HbIiqgRQdYUHK3RyGrENrDV9QkwArt6IAQ==", + "dev": true, + "dependencies": { + "execa": "^4.0.3" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/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, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "dev": true + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dev": true, + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dnd-core": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-11.1.3.tgz", + "integrity": "sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA==", + "dev": true, + "dependencies": { + "@react-dnd/asap": "^4.0.0", + "@react-dnd/invariant": "^2.0.0", + "redux": "^4.0.4" + } + }, + "node_modules/dnd-multi-backend": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/dnd-multi-backend/-/dnd-multi-backend-6.0.0.tgz", + "integrity": "sha512-qfUO4V0IACs24xfE9m9OUnwIzoL+SWzSiFbKVIHE0pFddJeZ93BZOdHS1XEYr8X3HNh+CfnfjezXgOMgjvh74g==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/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, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.3.619", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.619.tgz", + "integrity": "sha512-WFGatwtk7Fw0QcKCZzfGD72hvbcXV8kLY8aFuj0Ip0QRnOtyLYMsc+wXbSjb2w4lk1gcAeNU1/lQ20A+tvuypQ==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "dev": true, + "dependencies": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.0.tgz", + "integrity": "sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", + "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.2.0", + "tapable": "^0.1.8" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "dev": true + }, + "node_modules/env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", + "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "dev": true, + "dependencies": { + "stackframe": "^1.1.1" + } + }, + "node_modules/es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz", + "integrity": "sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.2.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-airbnb": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz", + "integrity": "sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^14.2.1", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-react": "^7.21.5", + "eslint-plugin-react-hooks": "^4 || ^3 || ^2.3.0 || ^1.7.0" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", + "eslint-plugin-import": "^2.22.1" + } + }, + "node_modules/eslint-config-airbnb-typescript": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-12.0.0.tgz", + "integrity": "sha512-TUCVru1Z09eKnVAX5i3XoNzjcCOU3nDQz2/jQGkg1jVYm+25fKClveziSl16celfCq+npU0MBPW/ZnXdGFZ9lw==", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "4.4.1", + "eslint-config-airbnb": "18.2.0", + "eslint-config-airbnb-base": "14.2.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.4.1" + } + }, + "node_modules/eslint-config-airbnb-typescript/node_modules/@typescript-eslint/parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.4.1.tgz", + "integrity": "sha512-S0fuX5lDku28Au9REYUsV+hdJpW/rNW0gWlc4SXzF/kdrRaAVX9YCxKpziH7djeWT/HFAjLZcnY7NJD8xTeUEg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "4.4.1", + "@typescript-eslint/types": "4.4.1", + "@typescript-eslint/typescript-estree": "4.4.1", + "debug": "^4.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-airbnb-typescript/node_modules/@typescript-eslint/scope-manager": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.4.1.tgz", + "integrity": "sha512-2oD/ZqD4Gj41UdFeWZxegH3cVEEH/Z6Bhr/XvwTtGv66737XkR4C9IqEkebCuqArqBJQSj4AgNHHiN1okzD/wQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.4.1", + "@typescript-eslint/visitor-keys": "4.4.1" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-airbnb-typescript/node_modules/@typescript-eslint/types": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.4.1.tgz", + "integrity": "sha512-KNDfH2bCyax5db+KKIZT4rfA8rEk5N0EJ8P0T5AJjo5xrV26UAzaiqoJCxeaibqc0c/IvZxp7v2g3difn2Pn3w==", + "dev": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-airbnb-typescript/node_modules/@typescript-eslint/typescript-estree": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.4.1.tgz", + "integrity": "sha512-wP/V7ScKzgSdtcY1a0pZYBoCxrCstLrgRQ2O9MmCUZDtmgxCO/TCqOTGRVwpP4/2hVfqMz/Vw1ZYrG8cVxvN3g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.4.1", + "@typescript-eslint/visitor-keys": "4.4.1", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-airbnb-typescript/node_modules/@typescript-eslint/visitor-keys": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.4.1.tgz", + "integrity": "sha512-H2JMWhLaJNeaylSnMSQFEhT/S/FsJbebQALmoJxMPMxLtlVAMy2uJP/Z543n9IizhjRayLSqoInehCeNW9rWcw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.4.1", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-airbnb-typescript/node_modules/eslint-config-airbnb": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.0.tgz", + "integrity": "sha512-Fz4JIUKkrhO0du2cg5opdyPKQXOI2MvF8KUvN2710nJMT6jaRUpRE2swrJftAjVGL7T1otLM5ieo5RqS1v9Udg==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^14.2.0", + "object.assign": "^4.1.0", + "object.entries": "^1.1.2" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", + "eslint-plugin-import": "^2.21.2", + "eslint-plugin-jsx-a11y": "^6.3.0", + "eslint-plugin-react": "^7.20.0", + "eslint-plugin-react-hooks": "^4 || ^3 || ^2.3.0 || ^1.7.0" + } + }, + "node_modules/eslint-config-airbnb-typescript/node_modules/eslint-config-airbnb-base": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz", + "integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.9", + "object.assign": "^4.1.0", + "object.entries": "^1.1.2" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", + "eslint-plugin-import": "^2.21.2" + } + }, + "node_modules/eslint-config-airbnb-typescript/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-config-prettier": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.0.0.tgz", + "integrity": "sha512-8Y8lGLVPPZdaNA7JXqnvETVC7IiVRgAP6afQu9gOQRn90YY3otMNh+x7Vr2vMePQntF+5erdSUBqSzCmU/AxaQ==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-react-app": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz", + "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0", + "@typescript-eslint/parser": "^4.0.0", + "babel-eslint": "^10.0.0", + "eslint": "^7.5.0", + "eslint-plugin-flowtype": "^5.2.0", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-jest": "^24.0.0", + "eslint-plugin-jsx-a11y": "^6.3.1", + "eslint-plugin-react": "^7.20.3", + "eslint-plugin-react-hooks": "^4.0.8", + "eslint-plugin-testing-library": "^3.9.0" + }, + "peerDependenciesMeta": { + "eslint-plugin-jest": { + "optional": true + }, + "eslint-plugin-testing-library": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "dependencies": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-import-resolver-webpack": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.0.tgz", + "integrity": "sha512-hZWGcmjaJZK/WSCYGI/y4+FMGQZT+cwW/1E/P4rDwFj2PbanlQHISViw4ccDJ+2wxAqjgwBfxwy3seABbVKDEw==", + "dev": true, + "dependencies": { + "array-find": "^1.0.0", + "debug": "^2.6.9", + "enhanced-resolve": "^0.9.1", + "find-root": "^1.1.0", + "has": "^1.0.3", + "interpret": "^1.2.0", + "lodash": "^4.17.15", + "node-libs-browser": "^1.0.0 || ^2.0.0", + "resolve": "^1.13.1", + "semver": "^5.7.1" + }, + "peerDependencies": { + "eslint-plugin-import": ">=1.4.0", + "webpack": ">=1.11.0" + } + }, + "node_modules/eslint-import-resolver-webpack/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-import-resolver-webpack/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "dependencies": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-module-utils/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-plugin-flowtype": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.2.0.tgz", + "integrity": "sha512-z7ULdTxuhlRJcEe1MVljePXricuPOrsWfScRXFhNzVD5dmTHWjIF57AxD0e7AbEoLSbjSsaA5S+hCg43WvpXJQ==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "dependencies": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-plugin-jest": { + "version": "24.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.1.3.tgz", + "integrity": "sha512-dNGGjzuEzCE3d5EPZQ/QGtmlMotqnYWD/QpCZ1UuZlrMAdhG5rldh0N0haCvhGnUkSeuORS5VNROwF9Hrgn3Lg==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "^4.0.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz", + "integrity": "sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.11.2", + "aria-query": "^4.2.2", + "array-includes": "^3.1.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.0.2", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.6", + "emoji-regex": "^9.0.0", + "has": "^1.0.3", + "jsx-ast-utils": "^3.1.0", + "language-tags": "^1.0.5" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz", + "integrity": "sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.1", + "array.prototype.flatmap": "^1.2.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "object.entries": "^1.1.2", + "object.fromentries": "^2.0.2", + "object.values": "^1.1.1", + "prop-types": "^15.7.2", + "resolve": "^1.18.1", + "string.prototype.matchall": "^4.0.2" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz", + "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-2.4.1.tgz", + "integrity": "sha512-cj8iPWZKuAiVD8MMgTSunyMCAvxQxp5mxoPHZl1UMGkApFXaXJHdCFcCR+oZEJbBNhReNa5SjESIn34uqUbBtg==", + "dev": true, + "dependencies": { + "@types/eslint": "^7.2.4", + "arrify": "^2.0.1", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0", + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "dependencies": { + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/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, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "dependencies": { + "original": "^1.0.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "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.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-rate-limit": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.2.3.tgz", + "integrity": "sha512-cjQH+oDrEPXxc569XvxhHC6QXqJiuBT6BhZ70X3bdAImcnHnTNMVuMAJaT0TXPoRiEErUrVPRcOTpZpM36VbOQ==", + "dev": true + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/express/node_modules/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 + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/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 + }, + "node_modules/fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-json-patch": { + "version": "3.0.0-1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.0.0-1.tgz", + "integrity": "sha512-6pdFb07cknxvPzCeLsFHStEy+MysPJPgZQ9LbQ/2O67unQF93SNqfdSqnPPl71YMHX+AD8gbl7iuoGFzHEdDuw==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fast-memoize": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", + "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, + "node_modules/fast-shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz", + "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==", + "dev": true + }, + "node_modules/fast-sort": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-2.2.0.tgz", + "integrity": "sha512-W7zqnn2zsYoQA87FKmYtgOsbJohOrh7XrtZrCVHN5XZKqTBTv5UG+rSS3+iWbg/nepRQUOu+wnas8BwtK8kiCg==", + "dev": true + }, + "node_modules/fastest-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz", + "integrity": "sha1-kSLUBtTJ2YvqZEpraFPVh0uHsCg=", + "dev": true + }, + "node_modules/fastq": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz", + "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/feedme": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/feedme/-/feedme-2.0.2.tgz", + "integrity": "sha512-0RNn0uLaSey8EThxgABR0T1Q47kSRatYnAXy1cfUc8/eypqXiAu38XGthuwwzb7mESCD9465k6Nym8D9AtB8HA==", + "dev": true, + "dependencies": { + "clarinet": "^0.12.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/feedsub": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/feedsub/-/feedsub-0.7.6.tgz", + "integrity": "sha512-6M8XMKPePkntE+ZRrnY/U/JVV7BLUuNHiSUfnTu8ArLWWi+CuRsqF5g4TbEruJLJLvt3Fkgj/o14o66xHMTT7g==", + "dev": true, + "dependencies": { + "feedme": "^2.0.2", + "miniget": "^4.0.0", + "newsemitter": "^1.0.2", + "tiny-typed-emitter": "^2.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", + "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/file-selector": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz", + "integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/filesize": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", + "integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/filestream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/filestream/-/filestream-5.0.0.tgz", + "integrity": "sha512-5H3RqSaJp12THfZiNWodYM7TiKfQvrpX+EIOrB1XvCceTys4yvfEIl8wDp+/yI8qj6Bxym8m0NYWwVXDAet/+A==", + "dev": true, + "dependencies": { + "readable-stream": "^3.4.0", + "typedarray-to-buffer": "^3.0.0" + } + }, + "node_modules/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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/node_modules/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, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", + "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz", + "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.5.5", + "chalk": "^2.4.1", + "micromatch": "^3.1.10", + "minimatch": "^3.0.4", + "semver": "^5.6.0", + "tapable": "^1.0.0", + "worker-rpc": "^0.1.0" + }, + "engines": { + "node": ">=6.11.5", + "yarn": ">=1.0.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "dev": true, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz", + "integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "dev": true, + "dependencies": { + "js-yaml": "^3.13.1" + } + }, + "node_modules/frontmatter-markdown-loader": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/frontmatter-markdown-loader/-/frontmatter-markdown-loader-3.6.2.tgz", + "integrity": "sha512-yZ4rQOIHgtGskRlUPsdJ5+Lo23mzyAdmFdN7JiXBzgfjrKIpOHnejmHw7EItHVRjlTwFNE1L7md4k8wmByJ33Q==", + "dev": true, + "dependencies": { + "front-matter": "^4.0.0", + "loader-utils": "^2.0.0", + "markdown-it": "^11.0.0" + } + }, + "node_modules/frontmatter-markdown-loader/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz", + "integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/fsevents": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", + "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "dependencies": { + "globule": "^1.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/geoip-country": { + "version": "4.0.46", + "resolved": "https://registry.npmjs.org/geoip-country/-/geoip-country-4.0.46.tgz", + "integrity": "sha512-hkgLJ4mUsJSZBtM0zcqnAMrui73hRwvUozm2F87Q1gH7oPGB33IC8GsDW7nigKDye/00Y7kQsq1g/SIiG8WLuw==", + "dependencies": { + "async": "^2.6.1", + "colors": "^1.4.0", + "glob": "^7.1.6", + "iconv-lite": "^0.5.2", + "ip-address": "^6.3.0", + "lazy": "^1.0.11", + "rimraf": "^2.7.1", + "yauzl": "^2.10.0" + }, + "engines": { + "node": ">=0.6.3" + } + }, + "node_modules/geoip-country/node_modules/iconv-lite": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/geoip-country/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/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, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-node-dimensions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz", + "integrity": "sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ==", + "dev": true + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-user-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-1.4.0.tgz", + "integrity": "sha512-gQo03lP1OArHLKlnoglqrGGl7b04u2EP9Xutmp72cMdtrrSD7ZgIsCsUKZynYWLDkVJW33Cj3pliP7uP0UonHQ==", + "dev": true, + "dependencies": { + "lodash.once": "^4.1.1" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dependencies": { + "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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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 + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/globule": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", + "dev": true, + "dependencies": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "dev": true, + "dependencies": { + "delegate": "^3.1.2" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "node_modules/gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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 + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-wasm": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.4.1.tgz", + "integrity": "sha512-6hbcJY74kQQOAoDQZgm0rku51jlf0f3+g+q+1CI3vNvabF27SQuVJm86FnMNMaw8WEjBeW4U5GWX1KsKd8qZCw==", + "dev": true + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/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, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/hpack.js/node_modules/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 + }, + "node_modules/hpack.js/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "node_modules/html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.0.0-alpha.15", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.0.0-alpha.15.tgz", + "integrity": "sha512-SFnpxiOo8EZ37fPUG4elnii78E7WBlv6pqJl0rwK5knWRpvzQbbPGmn/hNrnBvgPbKi5RHwoaEdsQkLvHk+AzA==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^5.0.0", + "html-minifier-terser": "^5.0.1", + "loader-utils": "2.0.0", + "lodash": "^4.17.20", + "pretty-error": "^2.1.1", + "tapable": "2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.1.2" + } + }, + "node_modules/html-webpack-plugin/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/html-webpack-plugin/node_modules/tapable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.0.0.tgz", + "integrity": "sha512-bjzn0C0RWoffnNdTzNi7rNDhs1Zlwk2tRXgk8EiHKAOX1Mag3d6T0Y5zNa7l9CJ+EoUne/0UHdwS8tMbkh9zDg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/http-parser-js": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", + "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.0.6.tgz", + "integrity": "sha512-NyL6ZB6cVni7pl+/IT2W0ni5ME00xR0sN27AQZZrpKn1b+qRh+mLbBxIq9Cq1oGfmTc7BUq4HB77mxwCaxAYNg==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.4", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "lodash": "^4.17.20", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "node_modules/immer": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/immer/-/immer-7.0.9.tgz", + "integrity": "sha512-Vs/gxoM4DqNAYR7pugIxi0Xc8XAun/uy7AQu4fLLqaTBHxjOP9pJ266Q9MWA/ly4z6rAFZbvViOtihxUZ7O28A==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/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, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inline-style-prefixer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-4.0.2.tgz", + "integrity": "sha512-N8nVhwfYga9MiV9jWlwfdj1UDIaZlBFu4cJSJkIr7tZX7sHpHhGR5su1qdpW+7KPL8ISTvCIkcaFi/JdBknvPg==", + "dev": true, + "dependencies": { + "bowser": "^1.7.3", + "css-in-js-utils": "^2.0.0" + } + }, + "node_modules/internal-ip": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", + "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", + "dev": true, + "dependencies": { + "default-gateway": "^6.0.0", + "ipaddr.js": "^1.9.1", + "is-ip": "^3.1.0", + "p-event": "^4.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/internal-ip?sponsor=1" + } + }, + "node_modules/internal-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz", + "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==", + "dev": true, + "dependencies": { + "es-abstract": "^1.17.0-next.1", + "has": "^1.0.3", + "side-channel": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internal-slot/node_modules/es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/intl-messageformat": { + "version": "9.3.20", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.3.20.tgz", + "integrity": "sha512-jmpjYHE076J/0CIofrPhtUC4LfmsAhuv4JMQxytl2KJd2bim+3+gQJh+Z1vyHUzcj4fIHdt388ZGchb8f0NwOA==", + "dev": true, + "dependencies": { + "fast-memoize": "^2.5.2", + "intl-messageformat-parser": "6.0.18", + "tslib": "^2.0.1" + } + }, + "node_modules/intl-messageformat-parser": { + "version": "6.0.18", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.18.tgz", + "integrity": "sha512-vLjACEunfi5uSUCWFLOR4PXQ9DGLpED3tM7o9zsYsOvjl0VIheoxyG0WZXnsnhn+S+Zu158M6CkuHXeNZfKRRg==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "node_modules/ip-address": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-6.4.0.tgz", + "integrity": "sha512-c5uxc2WUTuRBVHT/6r4m7HIr/DfV0bF6DvLH3iZGSK8wp8iMwwZSgIq2do0asFf8q9ECug0SE+6+1ACMe4sorA==", + "dependencies": { + "jsbn": "1.1.0", + "lodash.find": "4.6.0", + "lodash.max": "4.0.1", + "lodash.merge": "4.6.2", + "lodash.padstart": "4.6.1", + "lodash.repeat": "4.1.0", + "sprintf-js": "1.1.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/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, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "dependencies": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "node_modules/is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-file/-/is-file-1.0.0.tgz", + "integrity": "sha1-KKRM+9nT2xkwRfIrZfzo7fliBZY=", + "dev": true + }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz", + "integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dev": true, + "dependencies": { + "ip-regex": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-ip/node_modules/ip-regex": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.2.0.tgz", + "integrity": "sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, + "node_modules/is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "dev": true, + "dependencies": { + "html-comment-regex": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.4.tgz", + "integrity": "sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-cli/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-cli/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/jest-cli/node_modules/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 + }, + "node_modules/jest-cli/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "fsevents": "^2.1.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "dependencies": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve/node_modules/parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-resolve/node_modules/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, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/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, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-resolve/node_modules/read-pkg/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "bin": { + "jest-runtime": "bin/jest-runtime.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runtime/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-runtime/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/jest-runtime/node_modules/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 + }, + "node_modules/jest-runtime/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", + "dev": true + }, + "node_modules/js-file-download": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.12.tgz", + "integrity": "sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=" + }, + "node_modules/jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "dependencies": { + "jsonify": "~0.0.0" + } + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "node_modules/json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dev": true, + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz", + "integrity": "sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.1", + "object.assign": "^4.1.1" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", + "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", + "dev": true, + "dependencies": { + "language-subtag-registry": "~0.3.2" + } + }, + "node_modules/lazy": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha1-2qBoIGKCVCwIgojpdcKXwa53tpA=", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/loader-runner": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.1.0.tgz", + "integrity": "sha512-oR4lB4WvwFoC70ocraKhn5nkKSs23t57h9udUgw8o0iH8hMXeEoRuUgfcvgUwAJ1ZpRqBvcou4N2SMvM1DwMrA==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/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, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/localforage": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz", + "integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==", + "dev": true, + "dependencies": { + "lie": "3.1.1" + } + }, + "node_modules/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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "node_modules/lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=", + "dev": true + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", + "dev": true + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=", + "dev": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "node_modules/lodash.max": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.max/-/lodash.max-4.0.1.tgz", + "integrity": "sha1-hzVWbGGLNan3YFILSHrnllivE2o=" + }, + "node_modules/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 + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, + "node_modules/lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" + }, + "node_modules/lodash.repeat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz", + "integrity": "sha1-/H3oEx2MisB+S0n3T/6CnR8r7EQ=" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loud-rejection": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-2.2.0.tgz", + "integrity": "sha512-S0FayMXku80toa5sZ6Ro4C+s+EtFDCsyJNG/AzFMfX3AxD5Si4dZsgzm/kKnbOxHl5Cv8jBlno8+3XYIh2pNjQ==", + "dev": true, + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/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, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "dependencies": { + "tmpl": "1.0.x" + } + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-it": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.1.tgz", + "integrity": "sha512-aU1TzmBKcWNNYvH9pjq6u92BML+Hz3h5S/QpfTFwiQF852pLT+9qHsrhM9JYipkOXZxGn+sGH8oyJE9FD9WezQ==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/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 + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.0.0.tgz", + "integrity": "sha512-qrcJOe6uD+EW8Wrci1Vdiua/15Xw3n/QnaNXE7varnB6InxSk7nu3/i5jfy3S6kWxr8WYJ6R1o0afMUtvorTsA==", + "dev": true, + "dependencies": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/mem?sponsor=1" + } + }, + "node_modules/mem/node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/memfs": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz", + "integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==", + "dev": true, + "dependencies": { + "fs-monkey": "1.0.1" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==", + "dev": true + }, + "node_modules/memory-fs": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", + "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", + "dev": true + }, + "node_modules/meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "dependencies": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/microevent.ts": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", + "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "dependencies": { + "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-create-react-context": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", + "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.1", + "tiny-warning": "^1.0.3" + }, + "peerDependencies": { + "prop-types": "^15.0.0", + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.3.tgz", + "integrity": "sha512-7lvliDSMiuZc81kI+5/qxvn47SCM7BehXex3f2c6l/pR3Goj58IQxZh9nuPQ3AkGQgoETyXuIqLDaO5Oa0TyBw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "webpack-sources": "^1.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.4.0 || ^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/miniget": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/miniget/-/miniget-4.1.0.tgz", + "integrity": "sha512-kzhrNv5L7LlomwGmPGQsLQ2PnT1LeJJWfB0wNFGyv426gEM1gsfziBQmfkr6XOBA8EusZg9nowlNT5CbuKTjZg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/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 + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mobx": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.0.4.tgz", + "integrity": "sha512-wT2QJT9tW19VSHo9x7RPKU3z/I2Ps6wUS8Kb1OO+kzmg7UY3n4AkcaYG6jq95Lp1R9ohjC/NGYuT2PtuvBjhFg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + } + }, + "node_modules/mobx-react": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-7.0.5.tgz", + "integrity": "sha512-WmHl3Ni30ujVcOOnllmGmyccsbfFCKtAwpkIwncwxhgLcvXcE0Wa9lGJIhoqQdTJzGr0AJqGzMVAdwNc3Fj2DQ==", + "dev": true, + "dependencies": { + "mobx-react-lite": "^3.1.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.0.4", + "react": "^16.8.0 || ^17" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/mobx-react-lite": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.1.6.tgz", + "integrity": "sha512-MM3x9BLt5nC7iE/ILA5n2+hfrplEKYbFjqROEuGkzBdZP/KD+Z44+2gseczRrTG0xFuiPWfEzgT68+6/zqOiEw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.0.4", + "react": "^16.8.0 || ^17" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dev": true, + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/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 + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "node_modules/multistream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.0.1.tgz", + "integrity": "sha512-LNPIR/LD0JUw2beGlSv4sgTSnGbZp16d/PG2rnIrYjkeCaepNmBTobuiaNQATCPiYgn+BBuQTm70UlvwRfLZ3Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "readable-stream": "^3.6.0" + } + }, + "node_modules/nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true + }, + "node_modules/nano-css": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.3.0.tgz", + "integrity": "sha512-uM/9NGK9/E9/sTpbIZ/bQ9xOLOIHZwrrb/CRlbDHBU/GFS7Gshl24v/WJhwsVViWkpOXUmiZ66XO7fSB4Wd92Q==", + "dev": true, + "dependencies": { + "css-tree": "^1.0.0-alpha.28", + "csstype": "^2.5.5", + "fastest-stable-stringify": "^1.0.1", + "inline-style-prefixer": "^4.0.0", + "rtl-css-js": "^1.9.0", + "sourcemap-codec": "^1.4.1", + "stacktrace-js": "^2.0.0", + "stylis": "3.5.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/nano-css/node_modules/csstype": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz", + "integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/native-url": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/native-url/-/native-url-0.2.6.tgz", + "integrity": "sha512-k4bDC87WtgrdD362gZz6zoiXQrl40kYlBmpfmSjwRO1VU0V5ccwJTlxuE72F6m3V0vc1xOf6n3UCP9QyerRqmA==", + "dev": true, + "dependencies": { + "querystring": "^0.2.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/nedb": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz", + "integrity": "sha1-DjUCzYLABNU1WkPJ5VV3vXvZHYg=", + "dev": true, + "dependencies": { + "async": "0.2.10", + "binary-search-tree": "0.2.5", + "localforage": "^1.3.0", + "mkdirp": "~0.5.1", + "underscore": "~1.4.4" + } + }, + "node_modules/nedb-promises": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/nedb-promises/-/nedb-promises-4.1.0.tgz", + "integrity": "sha512-nTdx7jX/Vu24L05Cy0ee7CL3L4SEHCb1jlLlegPl0VlE8jsUXgnSyNOjq3FEc3cdUSDK05X7hSzb3+a07PigmQ==", + "dev": true, + "dependencies": { + "nedb": "^1.8.0" + } + }, + "node_modules/nedb/node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "node_modules/nedb/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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 + }, + "node_modules/newsemitter": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/newsemitter/-/newsemitter-1.0.2.tgz", + "integrity": "sha512-N9jDBdccPetXfeM5/VkUYZewdVLQ3FzcjCuMqkJCze3tDEViP9cba0aK0oHl19Jj7GkfNxsEtntRhIzgAJ8HiA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-gyp": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", + "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/node-libs-browser/node_modules/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 + }, + "node_modules/node-libs-browser/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-notifier": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz", + "integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==", + "dev": true, + "optional": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-releases": { + "version": "1.1.67", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz", + "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==", + "dev": true + }, + "node_modules/node-sass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-5.0.0.tgz", + "integrity": "sha512-opNgmlu83ZCF792U281Ry7tak9IbVC+AKnXGovcQ8LG8wFaJv6cLnRlc6DIHlmNxWEexB5bZxi9SZ9JyUuOYjw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^7.0.3", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "lodash": "^4.17.15", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^7.1.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "2.2.5", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "bin": { + "node-sass": "bin/node-sass" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-sass/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-sass/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-sass/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-sass/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/node-sass/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/node-sass/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/node-sass/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz", + "integrity": "sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", + "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.3.tgz", + "integrity": "sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", + "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", + "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz", + "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "dependencies": { + "url-parse": "^1.4.3" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "node_modules/overlayscrollbars": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.0.tgz", + "integrity": "sha512-p8oHrMeRAKxXDMPI/EBNITj/zTVHKNnAnM59Im+xnoZUlV07FyTg46wom2286jJlXGGfcPFG/ba5NUiCwWNd4w==", + "dev": true + }, + "node_modules/overlayscrollbars-react": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/overlayscrollbars-react/-/overlayscrollbars-react-0.2.2.tgz", + "integrity": "sha512-sRJDaKIxD+No6ygMRaCxejuIH2CksSCUTfaDOzDhPt22lI3IPZq/+Ifw2RT4j+U7hgXLn9P5QqA00f2bsZVwPA==", + "dev": true, + "peerDependencies": { + "overlayscrollbars": "^1.10.0", + "react": "^16.4.0" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "dependencies": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/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, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module/node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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 + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/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, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/passport": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", + "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", + "dev": true, + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz", + "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==", + "dev": true, + "dependencies": { + "jsonwebtoken": "^8.2.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=", + "dev": true + }, + "node_modules/pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/piece-length": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/piece-length/-/piece-length-2.0.1.tgz", + "integrity": "sha512-dBILiDmm43y0JPISWEmVGKBETQjwJe6mSU9GND+P9KW0SJGUwoU/odyH1nbalOP9i8WSYuqf1lQnaj92Bhw+Ug==", + "dev": true + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "dependencies": { + "node-modules-regexp": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.1.tgz", + "integrity": "sha512-RhsqOOAQzTgh1UB/IZdca7F9WDb7SUCR2Vnv1x7DbvuuggQIpoDwjK+q0rzoPffhYvWNKX5JSwS4so4K3UC6vA==", + "dev": true, + "dependencies": { + "colorette": "^1.2.1", + "nanoid": "^3.1.20", + "source-map": "^0.6.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/postcss-calc/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-calc/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-calc/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-calc/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-calc/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-calc/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-calc/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-calc/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-calc/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-colormin/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-colormin/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-colormin/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-colormin/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-colormin/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-colormin/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-colormin/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-colormin/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-colormin/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-colormin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-convert-values/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-convert-values/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-convert-values/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-convert-values/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-convert-values/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-convert-values/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-convert-values/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-convert-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-convert-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-comments/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-comments/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-comments/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-discard-comments/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-discard-comments/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-comments/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-comments/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-discard-duplicates/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-empty/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-empty/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-empty/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-discard-empty/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-discard-empty/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-empty/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-empty/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-overridden/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-overridden/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-overridden/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-discard-overridden/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-discard-overridden/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-discard-overridden/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-discard-overridden/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.1.0.tgz", + "integrity": "sha512-vbCkP70F3Q9PIk6d47aBwjqAMI4LfkXCoyxj+7NPNuVIwfTGdzv2KVQes59/RuxMniIgsYQCFSY42P3+ykJfaw==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/postcss-loader/node_modules/cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-loader/node_modules/import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-loader/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/postcss-loader/node_modules/parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "dependencies": { + "@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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/postcss-loader/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/postcss-loader/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "dependencies": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-merge-longhand/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-merge-longhand/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-merge-longhand/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-merge-longhand/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-merge-longhand/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-merge-longhand/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-merge-rules/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-merge-rules/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-merge-rules/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-merge-rules/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-merge-rules/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-merge-rules/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-font-values/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-font-values/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-font-values/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-minify-font-values/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-minify-font-values/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-minify-font-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-gradients/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-gradients/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-gradients/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-minify-gradients/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-minify-gradients/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-minify-gradients/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-params/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-params/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-params/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-params/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-minify-params/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-minify-params/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-params/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-minify-params/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-params/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-selectors/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-selectors/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-selectors/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-minify-selectors/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-minify-selectors/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss-minify-selectors/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/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, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/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, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/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, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-charset/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-charset/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-charset/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-normalize-charset/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-normalize-charset/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-charset/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-charset/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-normalize-display-values/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-normalize-display-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-positions/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-positions/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-positions/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-normalize-positions/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-normalize-positions/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-normalize-positions/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-normalize-repeat-style/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-normalize-repeat-style/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "dependencies": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-string/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-string/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-string/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-normalize-string/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-normalize-string/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-normalize-string/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-normalize-timing-functions/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-normalize-timing-functions/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-normalize-unicode/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-normalize-unicode/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-url/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-url/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-url/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-normalize-url/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-normalize-url/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-normalize-url/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-normalize-whitespace/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-normalize-whitespace/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-ordered-values/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-ordered-values/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-ordered-values/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-ordered-values/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-ordered-values/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-ordered-values/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-initial/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-initial/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-initial/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-reduce-initial/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-reduce-initial/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-initial/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-reduce-initial/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-reduce-transforms/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-reduce-transforms/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", + "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "dev": true, + "dependencies": { + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-svgo/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-svgo/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-svgo/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-svgo/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "node_modules/postcss-svgo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-unique-selectors/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-unique-selectors/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-unique-selectors/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/postcss-unique-selectors/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/postcss-unique-selectors/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-unique-selectors/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-unique-selectors/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/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 + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "dev": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/query-string": { + "version": "6.13.7", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.7.tgz", + "integrity": "sha512-CsGs8ZYb39zu0WLkeOhe0NMePqgYdAuCqxOYKDR5LVCytDZYMGx3Bb+xypvQvPHVPijRXB0HZNFllCzHRe4gEA==", + "dev": true, + "dependencies": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz", + "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/react": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz", + "integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dev-utils": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.1.tgz", + "integrity": "sha512-rlgpCupaW6qQqvu0hvv2FDv40QG427fjghV56XyPcP5aKtOAPzNAhQ7bHqk1YdS2vpW1W7aSV3JobedxuPlBAA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.10.4", + "address": "1.1.2", + "browserslist": "4.14.2", + "chalk": "2.4.2", + "cross-spawn": "7.0.3", + "detect-port-alt": "1.1.6", + "escape-string-regexp": "2.0.0", + "filesize": "6.1.0", + "find-up": "4.1.0", + "fork-ts-checker-webpack-plugin": "4.1.6", + "global-modules": "2.0.0", + "globby": "11.0.1", + "gzip-size": "5.1.1", + "immer": "7.0.9", + "is-root": "2.1.0", + "loader-utils": "2.0.0", + "open": "^7.0.2", + "pkg-up": "3.1.0", + "prompts": "2.4.0", + "react-error-overlay": "^6.0.8", + "recursive-readdir": "2.2.2", + "shell-quote": "1.7.2", + "strip-ansi": "6.0.0", + "text-table": "0.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-dev-utils/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-dev-utils/node_modules/browserslist": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz", + "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001125", + "electron-to-chromium": "^1.3.564", + "escalade": "^3.0.2", + "node-releases": "^1.1.61" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + }, + "node_modules/react-dev-utils/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-dev-utils/node_modules/chalk/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/react-dev-utils/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/react-dev-utils/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/react-dev-utils/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/react-dev-utils/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-dnd": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz", + "integrity": "sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==", + "dev": true, + "dependencies": { + "@react-dnd/shallowequal": "^2.0.0", + "@types/hoist-non-react-statics": "^3.3.1", + "dnd-core": "^11.1.3", + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "react": ">= 16.9.0", + "react-dom": ">= 16.9.0" + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz", + "integrity": "sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw==", + "dev": true, + "dependencies": { + "dnd-core": "^11.1.3" + } + }, + "node_modules/react-dnd-multi-backend": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-dnd-multi-backend/-/react-dnd-multi-backend-6.0.2.tgz", + "integrity": "sha512-SwpqRv0HkJYu244FbHf9NbvGzGy14Ir9wIAhm909uvOVaHgsOq6I1THMSWSgpwUI31J3Bo5uS19tuvGpVPjzZw==", + "dev": true, + "dependencies": { + "dnd-multi-backend": "^6.0.0", + "prop-types": "^15.7.2", + "react-dnd-preview": "^6.0.2" + }, + "peerDependencies": { + "react": "^16.13", + "react-dnd-html5-backend": "^11.1.3", + "react-dnd-touch-backend": "^11.1.3", + "react-dom": "^16.13" + } + }, + "node_modules/react-dnd-preview": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-dnd-preview/-/react-dnd-preview-6.0.2.tgz", + "integrity": "sha512-F2+uK4Be+q+7mZfNh9kaZols7wp1hX6G7UBTVaTpDsBpMhjFvY7/v7odxYSerSFBShh23MJl33a4XOVRFj1zoQ==", + "dev": true, + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^16.13.1", + "react-dnd": "^11.1.3" + } + }, + "node_modules/react-dnd-touch-backend": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-11.1.3.tgz", + "integrity": "sha512-8lz4fxfYwUuJ6Y2seQYwh8+OfwKcbBX0CIbz7AwXfBYz54Wg2nIDU6CP8Dyybt/Wyx4D3oXmTPEaOMB62uqJvQ==", + "dev": true, + "dependencies": { + "@react-dnd/invariant": "^2.0.0", + "dnd-core": "^11.1.3" + } + }, + "node_modules/react-dom": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz", + "integrity": "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.1" + }, + "peerDependencies": { + "react": "17.0.1" + } + }, + "node_modules/react-dropzone": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.2.4.tgz", + "integrity": "sha512-EGSvK2CxFTuc28WxwuJCICyuYFX8b+sRumwU6Bs6sTbElV2HtQkT0d6C+HEee6XfbjiLIZ+Th9uji27rvo2wGw==", + "dev": true, + "dependencies": { + "attr-accept": "^2.2.1", + "file-selector": "^0.2.2", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "react": ">= 16.8" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.8.tgz", + "integrity": "sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw==", + "dev": true + }, + "node_modules/react-intl": { + "version": "5.10.6", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.10.6.tgz", + "integrity": "sha512-IWhPTGGggs/n/OKkhEHAZ7rCfQ8m/2hmYIwJtOPuNQVyKKU+R863q4xP/+uCW1NOXB+yvbF2p7CB/v2hkuEVCA==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.5.0", + "@formatjs/intl": "1.4.10", + "@formatjs/intl-displaynames": "4.0.1", + "@formatjs/intl-listformat": "5.0.1", + "@formatjs/intl-relativetimeformat": "8.0.0", + "@types/hoist-non-react-statics": "^3.3.1", + "fast-memoize": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.3.20", + "intl-messageformat-parser": "6.0.18", + "shallow-equal": "^1.2.1", + "tslib": "^2.0.1" + }, + "peerDependencies": { + "react": "^16.3.0 || 17", + "typescript": "4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "node_modules/react-measure": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.5.2.tgz", + "integrity": "sha512-M+rpbTLWJ3FD6FXvYV6YEGvQ5tMayQ3fGrZhRPHrE9bVlBYfDCLuDcgNttYfk8IqfOI03jz6cbpqMRTUclQnaA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.2.0", + "get-node-dimensions": "^1.2.1", + "prop-types": "^15.6.2", + "resize-observer-polyfill": "^1.5.0" + }, + "peerDependencies": { + "react": ">0.13.0", + "react-dom": ">0.13.0" + } + }, + "node_modules/react-refresh": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", + "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", + "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.4.0", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router-dom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", + "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "node_modules/react-router/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/react-router/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-universal-interface": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "dev": true, + "peerDependencies": { + "react": "*", + "tslib": "*" + } + }, + "node_modules/react-use": { + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-15.3.4.tgz", + "integrity": "sha512-cHq1dELW6122oi1+xX7lwNyE/ugZs5L902BuO8eFJCfn2api1KeuPVG1M/GJouVARoUf54S2dYFMKo5nQXdTag==", + "dev": true, + "dependencies": { + "@types/js-cookie": "2.2.6", + "@xobotyi/scrollbar-width": "1.9.5", + "copy-to-clipboard": "^3.2.0", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.2.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.0.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^2.1.0", + "ts-easing": "^0.2.0", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0", + "react-dom": "^16.8.0" + } + }, + "node_modules/react-window": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.6.tgz", + "integrity": "sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + }, + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "dependencies": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "dependencies": { + "pify": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", + "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "dev": true, + "dependencies": { + "minimatch": "3.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "dependencies": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redent/node_modules/indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/redux": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", + "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, + "node_modules/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 + }, + "node_modules/regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags/node_modules/es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "node_modules/renderkid": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.4.tgz", + "integrity": "sha512-K2eXrSOJdq+HuKzlcjOlGoOarUu5SDguDEhE7+Ah4zuOWL40j8A/oHvLlLob9PSTNvVnBd+/q0Er1QfpEuem5g==", + "dev": true, + "dependencies": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "lodash": "^4.17.20", + "strip-ansi": "^3.0.0" + } + }, + "node_modules/renderkid/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/renderkid/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/request-promise-native/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", + "dev": true + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/ress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ress/-/ress-3.0.0.tgz", + "integrity": "sha512-MTPto7t44AawqmSbEmvMKoSMWPnxjaTuHf94s7RjWxuSGFN0o8/b+6yOwkaC50+Vihjsu6ODUEQR397gTMn57w==", + "dev": true + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, + "engines": { + "node": "6.* || >= 7.*" + } + }, + "node_modules/rtl-css-js": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.14.0.tgz", + "integrity": "sha512-Dl5xDTeN3e7scU1cWX8c9b6/Nqz3u/HgR4gePc1kWXYiQWVQbKCEyK6+Hxve9LbcJ5EieHy1J9nJCN3grTtGwg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/run-parallel": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", + "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rusha": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.13.tgz", + "integrity": "sha1-mghOe4YLF7/zAVuSxnpqM2GRUTo=", + "dev": true + }, + "node_modules/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, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "dependencies": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "bin": { + "sane": "src/cli.js" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/sane/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/sane/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/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, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/sane/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sane/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/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, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sane/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/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, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sane/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/sane/node_modules/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, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sass-graph": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", + "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^13.3.2" + }, + "bin": { + "sassgraph": "bin/sassgraph" + } + }, + "node_modules/sass-graph/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-graph/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sass-graph/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-graph/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/sass-graph/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/sass-graph/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/sass-graph/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/sass-graph/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-graph/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/sass-graph/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-graph/node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sass-graph/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-graph/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/sass-graph/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-graph/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-graph/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sass-graph/node_modules/y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "node_modules/sass-graph/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/sass-graph/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/sass-loader": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.1.0.tgz", + "integrity": "sha512-ZCKAlczLBbFd3aGAhowpYEy69Te3Z68cg8bnHHl6WnSCvnKpbM6pQrz957HWMa8LKVuhnD9uMplmMAHwGQtHeg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sass-loader/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/sass-loader/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/sass-loader/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/saxen": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/saxen/-/saxen-8.1.2.tgz", + "integrity": "sha512-xUOiiFbc3Ow7p8KMxwsGICPx46ZQvy3+qfNVhrkwfz3Vvq45eGt98Ft5IQaA1R/7Tb5B5MKh9fUR9x3c3nDTxw==", + "dev": true + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.1.tgz", + "integrity": "sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/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, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/screenfull": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.0.2.tgz", + "integrity": "sha512-cCF2b+L/mnEiORLN5xSAz6H3t18i2oHh9BA8+CQlAh5DRw2+NFAGQJOSYbcGw8B2k04g/lVvFcfZ83b3ysH5UQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "dependencies": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + } + }, + "node_modules/scss-tokenizer/node_modules/source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", + "dev": true + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "node_modules/selfsigned": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", + "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", + "dev": true, + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "dependencies": { + "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.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/send/node_modules/http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serialize-query-params": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/serialize-query-params/-/serialize-query-params-1.2.4.tgz", + "integrity": "sha512-m4hGkOY5y+ksPDSEkw12cNxt3HRUJv5G6oF9/4yq+GCw4LznudxC73qnz++VTHqXa0j1x1/iaBIpoiMBxr6w2w==", + "dev": true, + "peerDependencies": { + "query-string": "^5.1.1 || ^6" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/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, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/set-harmonic-interval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz", + "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==", + "dev": true, + "engines": { + "node": ">=6.9" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, + "node_modules/side-channel": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", + "dev": true, + "dependencies": { + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "node_modules/simple-sha1": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/simple-sha1/-/simple-sha1-3.0.1.tgz", + "integrity": "sha512-q7ehqWfHc1VhOm7sW099YDZ4I0yYX7rqyhqqhHV1IYeUTjPOhHyD3mXvv8k2P+rO7+7c8R4/D+8ffzC9BE7Cqg==", + "dev": true, + "dependencies": { + "queue-microtask": "^1.1.2", + "rusha": "^0.8.1" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/slice-ansi/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/slice-ansi/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/slice-ansi/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/sockjs": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", + "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^3.4.0", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs-client": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.0.tgz", + "integrity": "sha512-8Dt3BDi4FYNrCFGTL/HtwVzkARrENdwOUf1ZoW/9p3M8lZdFT35jVdrHza+qgxuG9H3/shR4cuX/X9umUrjP8Q==", + "dev": true, + "dependencies": { + "debug": "^3.2.6", + "eventsource": "^1.0.7", + "faye-websocket": "^0.11.3", + "inherits": "^2.0.4", + "json3": "^3.3.3", + "url-parse": "^1.4.7" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-1.1.3.tgz", + "integrity": "sha512-6YHeF+XzDOrT/ycFJNI53cgEsp/tHTMl37hi7uVyqFAlTXW109JazaQCkbc+jjoL2637qkH1amLi+JzrIpt5lA==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.2", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "source-map": "^0.6.1", + "whatwg-mimetype": "^2.3.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/source-map-loader/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/source-map-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "node_modules/ssri": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", + "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "node_modules/stack-generator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.5.tgz", + "integrity": "sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q==", + "dev": true, + "dependencies": { + "stackframe": "^1.1.1" + } + }, + "node_modules/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stackframe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", + "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", + "dev": true + }, + "node_modules/stacktrace-gps": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz", + "integrity": "sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg==", + "dev": true, + "dependencies": { + "source-map": "0.5.6", + "stackframe": "^1.1.1" + } + }, + "node_modules/stacktrace-gps/node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dev": true, + "dependencies": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/std-env": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.2.1.tgz", + "integrity": "sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ==", + "dev": true, + "dependencies": { + "ci-info": "^1.6.0" + } + }, + "node_modules/std-env/node_modules/ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "node_modules/stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/stdout-stream/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/stdout-stream/node_modules/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 + }, + "node_modules/stdout-stream/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-browserify/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/stream-browserify/node_modules/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 + }, + "node_modules/stream-browserify/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-http/node_modules/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, + "dependencies": { + "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" + } + }, + "node_modules/stream-http/node_modules/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 + }, + "node_modules/stream-http/node_modules/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, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", + "dev": true + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz", + "integrity": "sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has-symbols": "^1.0.1", + "internal-slot": "^1.0.2", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/style-loader/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/style-loader/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/stylehacks/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/stylehacks/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/stylehacks/node_modules/chalk/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/stylehacks/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/stylehacks/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/stylehacks/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/stylehacks/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/stylehacks/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/stylehacks/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylehacks/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylehacks/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stylis": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.0.tgz", + "integrity": "sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw==", + "dev": true + }, + "node_modules/superagent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 7.0.0" + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/superagent/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/supertest": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.0.1.tgz", + "integrity": "sha512-8yDNdm+bbAN/jeDdXsRipbq9qMpVF7wRsbwLgsANHqdjPsCoecmlTuqEcLQMGpmojFBhxayZ0ckXmLXYq7e+0g==", + "dev": true, + "dependencies": { + "methods": "1.1.2", + "superagent": "6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/svgo/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/svgo/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo/node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "node_modules/svgo/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/svgo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svgo/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "dependencies": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/table/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tapable": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tar": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", + "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.0.3.tgz", + "integrity": "sha512-zFdGk8Lh9ZJGPxxPE6jwysOlATWB8GMW8HcfGULWA/nPal+3VdATflQvSBSLQJRCmYZnfFJl6vkRTiwJGNgPiQ==", + "dev": true, + "dependencies": { + "jest-worker": "^26.6.1", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.3.8" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/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 + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/terser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.5.1.tgz", + "integrity": "sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin/node_modules/terser/node_modules/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, + "engines": { + "node": ">= 8" + } + }, + "node_modules/terser/node_modules/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 + }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, + "node_modules/throttle-debounce": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", + "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "dev": true + }, + "node_modules/tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==", + "dev": true + }, + "node_modules/tiny-typed-emitter": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.0.3.tgz", + "integrity": "sha512-MaCqhHlp6EAWN25yqBlajgd4scxxI2eJr7+EgoUAOV9UkMU3us/yp2bEnc2yOvyeDF8TUWuaz3zZCPGTKFJIpA==", + "dev": true + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=", + "dev": true + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "dependencies": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tr46/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "dependencies": { + "glob": "^7.1.2" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "dev": true, + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/ts-easing": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", + "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==", + "dev": true + }, + "node_modules/ts-jest": { + "version": "26.4.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.4.4.tgz", + "integrity": "sha512-3lFWKbLxJm34QxyVNNCgXX1u4o/RV0myvA2y2Bxm46iGIjKlaY0own9gIckbjZJPn+WaJEnfPPJ20HHGpoq4yg==", + "dev": true, + "dependencies": { + "@types/jest": "26.x", + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^26.1.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "mkdirp": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "jest": ">=26 <27", + "typescript": ">=3.8 <5.0" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "dependencies": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": ">=2.7" + } + }, + "node_modules/ts-node-dev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.1.tgz", + "integrity": "sha512-kAO8LUZgXZSY0+PucMPsQ0Bbdv0x+lgbN7j8gcD4PuTI4uKC6YchekaspmYTBNilkiu+rQYkWJA7cK+Q8/B0tQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.4.0", + "dateformat": "~1.0.4-1.2.3", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^9.0.0", + "tsconfig": "^7.0.0" + }, + "bin": { + "ts-node-dev": "lib/bin.js", + "tsnd": "lib/bin.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "peerDependencies": { + "node-notifier": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/ts-node-dev/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tsconfig/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tsconfig/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-1.3.1.tgz", + "integrity": "sha512-2h7utWyXgd2R2u2IuL8B4yu1gqMxbgUj2VS/MGVbFhEVQNJKXoQQoS5CBMh+eW31zFeSmDfEQ3qQf4xy5SlPVQ==", + "dev": true + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/use-query-params": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/use-query-params/-/use-query-params-1.1.9.tgz", + "integrity": "sha512-WAJ1GrKbFWv1TBn1RQpHqAwC7yyJsLaJjBhIfefrbY/h6mFSngzBQKirJndYwCS1ry77EwhpR/tQi5iovXWvuw==", + "dev": true, + "dependencies": { + "serialize-query-params": "^1.2.3" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", + "dev": true + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/util.promisify/node_modules/es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "dependencies": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "dev": true, + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.0.0.tgz", + "integrity": "sha512-fLL2rFuQpMtm9r8hrAV2apXX/WqHJ6+IC4/eQVdMDGBUgH/YMV4Gv3duk3kjmyg6uiQWBAA9nJwue4iJUOkHeA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/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, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "dependencies": { + "makeerror": "1.0.x" + } + }, + "node_modules/watchpack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.0.tgz", + "integrity": "sha512-UjgD1mqjkG99+3lgG36at4wPnUXNvis2v1utwTgQ43C22c4LD71LsYMExdWXh4HZ+RmW+B0t1Vrg2GpXAkTOQw==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/webpack": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.10.1.tgz", + "integrity": "sha512-mHu4iM2mW7d/8R91VPPNtUCNd1D8k51TTb4e0XjylapIR6WEmW8XUTBZq8TqmShj9XYxVXJn6AzKlWnrlty6DA==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.45", + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-module-context": "1.9.1", + "@webassemblyjs/wasm-edit": "1.9.1", + "@webassemblyjs/wasm-parser": "1.9.1", + "acorn": "^8.0.4", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.3.1", + "eslint-scope": "^5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.1.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "pkg-dir": "^5.0.0", + "schema-utils": "^3.0.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.0.3", + "watchpack": "^2.0.0", + "webpack-sources": "^2.1.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-4.0.2.tgz", + "integrity": "sha512-xyAICqIugWtT1RRH5aMMmZlPhDhEqPTDL0TWhmMZsuZ+cFlAvRxv4thCbuxdk9MW+OYK4c9BkfmgdQ1/7imkJA==", + "dev": true, + "dependencies": { + "mem": "^8.0.0", + "memfs": "^3.2.0", + "mime-types": "^2.1.27", + "range-parser": "^1.2.1", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.0.0-beta.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.0.0-beta.0.tgz", + "integrity": "sha512-mVD4Hn3bsMdcq6qE0y8xvH6KAu9NwS6F0NNgFe+n6gbsTQ7YgffUDydvy2iieyyKjAcBJDT5PZexv9tKv8kTNQ==", + "dev": true, + "dependencies": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^3.4.3", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "del": "^6.0.0", + "express": "^4.17.1", + "find-cache-dir": "^3.3.1", + "graceful-fs": "^4.2.4", + "html-entities": "^1.3.1", + "http-proxy-middleware": "^1.0.6", + "internal-ip": "^6.2.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "open": "^7.3.0", + "p-retry": "^4.2.0", + "portfinder": "^1.0.28", + "schema-utils": "^3.0.0", + "selfsigned": "^1.10.8", + "serve-index": "^1.9.1", + "sockjs": "0.3.21", + "sockjs-client": "1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^6.0.0", + "url": "^0.11.0", + "util": "^0.12.3", + "webpack-dev-middleware": "^4.0.2", + "ws": "^7.4.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 10.13.0" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/util": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", + "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.4.tgz", + "integrity": "sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/enhanced-resolve": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.4.0.tgz", + "integrity": "sha512-ZmqfWURB2lConOBM1JdCVfPyMRv5RdKWktLXO6123p97ovVm2CLBgw9t5MBj3jJWA6eHyOeIws9iJQoGFR4euQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webpack/node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack/node_modules/webpack-sources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", + "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpackbar": { + "version": "5.0.0-3", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz", + "integrity": "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.1.0", + "consola": "^2.15.0", + "figures": "^3.2.0", + "pretty-time": "^1.1.0", + "std-env": "^2.2.1", + "text-table": "^0.2.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "webpack": "3 || 4 || 5" + } + }, + "node_modules/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, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", + "dev": true, + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/worker-rpc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", + "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", + "dev": true, + "dependencies": { + "microevent.ts": "~0.1.1" + } + }, + "node_modules/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, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/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 + }, + "node_modules/wrap-ansi/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", + "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlbuilder": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", + "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/xmlrpc": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/xmlrpc/-/xmlrpc-1.3.2.tgz", + "integrity": "sha1-JrLqNHhI0Ciqx+dRS1NRl23j6D0=", + "dev": true, + "dependencies": { + "sax": "1.2.x", + "xmlbuilder": "8.2.x" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.0.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/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 + }, + "node_modules/yargs/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/zod/-/zod-1.11.11.tgz", + "integrity": "sha512-q1YeBpu+c7eUX5fDFMyfP97sD74TUQ+UN8va/nvbxnArr5euYsNO6fjiY0SdDkHKNZ+xBR2ZQToaeLgJ6fsB2A==", + "dev": true + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/compat-data": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", + "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==", + "dev": true + }, + "@babel/core": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", + "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.10", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "semver": "^5.4.1", + "source-map": "^0.5.0" + } + }, + "@babel/generator": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz", + "integrity": "sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-react-jsx": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz", + "integrity": "sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-react-jsx-experimental": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.10.tgz", + "integrity": "sha512-3Kcr2LGpL7CTRDTTYm1bzeor9qZbxbvU2AxsLA6mUG9gYarSfIKMK0UlU+azLWI+s0+BH768bwyaziWB2NOJlQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.10", + "@babel/helper-module-imports": "^7.12.5", + "@babel/types": "^7.12.10" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", + "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.12.5", + "@babel/helper-validator-option": "^7.12.1", + "browserslist": "^4.14.5", + "semver": "^5.5.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", + "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", + "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "regexpu-core": "^4.7.1" + } + }, + "@babel/helper-define-map": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", + "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", + "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.7" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.7.tgz", + "integrity": "sha512-I5xc9oSJ2h59OwyUqjv95HRyzxj53DAubUERgQMrpcCEYQyToeHA+NEcUEsVWB4j53RDeskeBJ0SgRAYHDBckw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", + "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz", + "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", + "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helpers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "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" + } + } + } + }, + "@babel/parser": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz", + "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.12.1", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", + "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.1.tgz", + "integrity": "sha512-knNIuusychgYN8fGJHONL0RbFxLGawhXOJNLBk75TniTsZZeA+wdkDuv6wp4lGwzQEKjZi6/WYtnb3udNPmQmQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-decorators": "^7.12.1" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", + "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", + "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", + "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", + "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", + "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", + "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", + "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", + "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", + "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", + "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz", + "integrity": "sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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-top-level-await": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", + "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz", + "integrity": "sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", + "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", + "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.12.1" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", + "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz", + "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", + "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", + "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", + "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", + "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", + "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", + "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", + "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", + "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", + "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", + "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", + "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", + "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.12.1", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", + "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.10.4", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-identifier": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", + "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", + "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", + "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", + "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", + "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", + "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz", + "integrity": "sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.10.tgz", + "integrity": "sha512-MM7/BC8QdHXM7Qc1wdnuk73R4gbuOpfrSUgfV/nODGc86sPY1tgmY2M9E9uAnf2e4DOIp8aKGWqgZfQxnTNGuw==", + "dev": true, + "requires": { + "@babel/helper-builder-react-jsx": "^7.10.4", + "@babel/helper-builder-react-jsx-experimental": "^7.12.10", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.12.1" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.7.tgz", + "integrity": "sha512-Rs3ETtMtR3VLXFeYRChle5SsP/P9Jp/6dsewBQfokDSzKJThlsuFcnzLTDRALiUmTC48ej19YD9uN1mupEeEDg==", + "dev": true, + "requires": { + "@babel/helper-builder-react-jsx-experimental": "^7.12.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.12.1" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz", + "integrity": "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", + "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", + "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", + "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", + "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", + "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", + "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz", + "integrity": "sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz", + "integrity": "sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-typescript": "^7.12.1" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", + "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", + "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/preset-env": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.10.tgz", + "integrity": "sha512-Gz9hnBT/tGeTE2DBNDkD7BiWRELZt+8lSysHuDwmYXUIvtwZl0zI+D6mZgXZX0u8YBlLS4tmai9ONNY9tjRgRA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.12.7", + "@babel/helper-compilation-targets": "^7.12.5", + "@babel/helper-module-imports": "^7.12.5", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-proposal-async-generator-functions": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-dynamic-import": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.1", + "@babel/plugin-proposal-json-strings": "^7.12.1", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-numeric-separator": "^7.12.7", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-class-properties": "^7.12.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.12.1", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-async-to-generator": "^7.12.1", + "@babel/plugin-transform-block-scoped-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.1", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-computed-properties": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-dotall-regex": "^7.12.1", + "@babel/plugin-transform-duplicate-keys": "^7.12.1", + "@babel/plugin-transform-exponentiation-operator": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-function-name": "^7.12.1", + "@babel/plugin-transform-literals": "^7.12.1", + "@babel/plugin-transform-member-expression-literals": "^7.12.1", + "@babel/plugin-transform-modules-amd": "^7.12.1", + "@babel/plugin-transform-modules-commonjs": "^7.12.1", + "@babel/plugin-transform-modules-systemjs": "^7.12.1", + "@babel/plugin-transform-modules-umd": "^7.12.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", + "@babel/plugin-transform-new-target": "^7.12.1", + "@babel/plugin-transform-object-super": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-property-literals": "^7.12.1", + "@babel/plugin-transform-regenerator": "^7.12.1", + "@babel/plugin-transform-reserved-words": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/plugin-transform-sticky-regex": "^7.12.7", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/plugin-transform-typeof-symbol": "^7.12.10", + "@babel/plugin-transform-unicode-escapes": "^7.12.1", + "@babel/plugin-transform-unicode-regex": "^7.12.1", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.12.10", + "core-js-compat": "^3.8.0", + "semver": "^5.5.0" + } + }, + "@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "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/preset-react": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.10.tgz", + "integrity": "sha512-vtQNjaHRl4DUpp+t+g4wvTHsLQuye+n0H/wsXIZRn69oz/fvNC7gQ4IK73zGJBaxvHoxElDvnYCthMcT7uzFoQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-transform-react-display-name": "^7.12.1", + "@babel/plugin-transform-react-jsx": "^7.12.10", + "@babel/plugin-transform-react-jsx-development": "^7.12.7", + "@babel/plugin-transform-react-pure-annotations": "^7.12.1" + } + }, + "@babel/preset-typescript": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.12.7.tgz", + "integrity": "sha512-nOoIqIqBmHBSEgBXWR4Dv/XBehtIFcw9PqZw6rFYuKrzsZmOQm3PR5siLBnKZFEsDb03IegG8nSjU/iXXXYRmw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-transform-typescript": "^7.12.1" + } + }, + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", + "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", + "dev": true, + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" + } + }, + "@babel/traverse": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.10", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@eslint/eslintrc": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", + "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "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 + } + } + }, + "@formatjs/cli": { + "version": "2.13.15", + "resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-2.13.15.tgz", + "integrity": "sha512-QyCS7hbge5VJ5lFNTrk1zvUW6Um+6gnKd+Vz7aSPr3fWvTBz3xfmdhacoHbC5dtJdiQOPCwX9x1Yy9VjnVfUAw==", + "dev": true, + "requires": { + "@formatjs/ts-transformer": "2.12.10", + "@types/json-stable-stringify": "^1.0.32", + "@types/lodash": "^4.14.150", + "@types/loud-rejection": "^2.0.0", + "@types/node": "14", + "chalk": "^4.0.0", + "commander": "^6.1.0", + "fast-glob": "^3.2.4", + "fs-extra": "^9.0.0", + "intl-messageformat-parser": "6.0.18", + "json-stable-stringify": "^1.0.1", + "lodash": "^4.17.15", + "loud-rejection": "^2.2.0", + "tslib": "^2.0.1", + "typescript": "^4.0" + }, + "dependencies": { + "@types/node": { + "version": "14.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.11.tgz", + "integrity": "sha512-BJ97wAUuU3NUiUCp44xzUFquQEvnk1wu7q4CMEUYKJWjdkr0YWYDsm4RFtAvxYsNjLsKcrFt6RvK8r+mnzMbEQ==", + "dev": true + } + } + }, + "@formatjs/ecma402-abstract": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz", + "integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==", + "dev": true, + "requires": { + "tslib": "^2.0.1" + } + }, + "@formatjs/intl": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-1.4.10.tgz", + "integrity": "sha512-CwbOmAnM2QKBUs6Eps1ry0YBe9nIQgQp9xQyxth/0BjJ8zRE3gIUzdNrLNCZ41nHuNPVFJRRIX79+yu5l+A56w==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "1.5.0", + "@formatjs/intl-datetimeformat": "3.1.0", + "@formatjs/intl-displaynames": "4.0.1", + "@formatjs/intl-listformat": "5.0.1", + "@formatjs/intl-relativetimeformat": "8.0.0", + "fast-memoize": "^2.5.2", + "intl-messageformat": "9.3.20", + "intl-messageformat-parser": "6.0.18", + "tslib": "^2.0.1" + } + }, + "@formatjs/intl-datetimeformat": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/intl-datetimeformat/-/intl-datetimeformat-3.1.0.tgz", + "integrity": "sha512-XKyDQ3xFgZK2w8GE2v+zE0nk/JqGKFE0UxTI716mp/+OVuws+dbQPiORfSrJceH7E3ZkfGrvO0BB8sksQNsZ+w==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } + }, + "@formatjs/intl-displaynames": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-4.0.1.tgz", + "integrity": "sha512-vhG9y+F0BudHU9ev0O9Tc5Uwz/MAcCzbBzceSnjcoUMyLLfFN6GSPBvU6+ocxWsfjhu/yL5ja+doZdhwDcSXrA==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } + }, + "@formatjs/intl-listformat": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-5.0.1.tgz", + "integrity": "sha512-x1gqI3xvTn8uTY0W+bL4ySW/5HFeQXkNNfsdoaRtX2b/HNa4fZoU1EaA6koAk9gUAWSR5Ofe1Ps49CXaMvwcTg==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } + }, + "@formatjs/intl-relativetimeformat": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-8.0.0.tgz", + "integrity": "sha512-GKJvd2+Sx0BJqsKt2rBbkgGAwfBjKVnvlRTZQ+OhgSEOeRBHOtaub1jUx8ScQoS5Xe0RFLvTLL2LSnajg6EXkw==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } + }, + "@formatjs/ts-transformer": { + "version": "2.12.10", + "resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.12.10.tgz", + "integrity": "sha512-H8mtPQcyXxLo3GJGkNVj3ZlmebeqxQfVTIvGsdpE1oXKZ/SxKqvC7ZeHlbZUyXUEiRwdJ4Hfsgw1QzsmTJnicw==", + "dev": true, + "requires": { + "intl-messageformat-parser": "6.0.18", + "tslib": "^2.0.1", + "typescript": "^4.0" + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^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 + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + } + }, + "@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + } + }, + "@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "node-notifier": "^8.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + } + }, + "@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", + "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4" + } + }, + "@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.3.tgz", + "integrity": "sha512-br5Qwvh8D2OQqSXpd1g/xqXKnK0r+Jz6qVKBbWmpUcrbGOxUrf39V5oZ1876084CGn18uMdR5uvPqBv9UqtBjQ==", + "dev": true, + "requires": { + "ansi-html": "^0.0.7", + "error-stack-parser": "^2.0.6", + "html-entities": "^1.2.1", + "native-url": "^0.2.6", + "schema-utils": "^2.6.5", + "source-map": "^0.7.3" + }, + "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 + } + } + }, + "@react-dnd/asap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.0.tgz", + "integrity": "sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ==", + "dev": true + }, + "@react-dnd/invariant": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", + "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==", + "dev": true + }, + "@react-dnd/shallowequal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", + "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@types/babel__core": { + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz", + "integrity": "sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz", + "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.0.tgz", + "integrity": "sha512-NTPErx4/FiPCGScH7foPyr+/1Dkzkni+rHiYHHoTjvwou7AQzJkNeD60A9CXRy+ZEN2B1bggmkTMCDb+Mv5k+A==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.11.0.tgz", + "integrity": "sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/bencode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/bencode/-/bencode-2.0.0.tgz", + "integrity": "sha512-ntDggX576d+MULpy9ApOy3OI9GqO86H+T9zEwYk3fdVaLi85M/1l+GVR/UWfITg9czcOO2SxZJyzyTOrI8UsFA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/classnames": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz", + "integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw==", + "dev": true + }, + "@types/clipboard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.1.tgz", + "integrity": "sha512-gJJX9Jjdt3bIAePQRRjYWG20dIhAgEqonguyHxXuqALxsoDsDLimihqrSg8fXgVTJ4KZCzkfglKtwsh/8dLfbA==", + "dev": true + }, + "@types/compression": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.0.tgz", + "integrity": "sha512-3LzWUM+3k3XdWOUk/RO+uSjv7YWOatYq2QADJntK1pjkk4DfVP0KrIEPDnXRJxAAGKe0VpIPRmlINLDuCedZWw==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-P1bffQfhD3O4LW0ioENXUhZ9OIa0Zn+P7M+pWgkCKaT53wVLSq0mrKksCID/FGHpFhRSxRGhgrQmfhRuzwtKdg==", + "dev": true + }, + "@types/cookie-parser": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz", + "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, + "@types/create-torrent": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/create-torrent/-/create-torrent-4.4.0.tgz", + "integrity": "sha512-ED4MMoZeSQvNt6IhIiCunXy27Yl25fXf3SENKX4FBU4d1dYDC/rF7wDGir8G829YjUln8vbw0Hqp3T1xWr3mKA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/d3": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-6.2.0.tgz", + "integrity": "sha512-XhQ6sCTu+CrFLqJMsg/uRPZQrt5FlCPjPE/wvsSBYoaOZ9C1chdJSS9+2oR8+Xtk6DKGewa7/UP5icJRwAryEA==", + "dev": true, + "requires": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "@types/d3-array": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-2.8.0.tgz", + "integrity": "sha512-Q0ubcGHAmCRPh90/hoYB4eKWhxYKUxphwSeQrlz2tiabQ8S9zqhaE2CZJtCaLH2cjqKcjr52WPvmOA7ha0O4ZA==", + "dev": true + }, + "@types/d3-axis": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-2.0.0.tgz", + "integrity": "sha512-gUdlEwGBLl3tXGiBnBNmNzph9W3bCfa4tBgWZD60Z1eDQKTY4zyCAcZ3LksignGfKawYatmDYcBdjJ5h/54sqA==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-brush": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-2.1.0.tgz", + "integrity": "sha512-rLQqxQeXWF4ArXi81GlV8HBNwJw9EDpz0jcWvvzv548EDE4tXrayBTOHYi/8Q4FZ/Df8PGXFzxpAVQmJMjOtvQ==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-chord": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-2.0.0.tgz", + "integrity": "sha512-3nHsLY7lImpZlM/hrPeDqqW2a+lRXXoHsG54QSurDGihZAIE/doQlohs0evoHrWOJqXyn4A4xbSVEtXnMEZZiw==", + "dev": true + }, + "@types/d3-color": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.1.tgz", + "integrity": "sha512-u7LTCL7RnaavFSmob2rIAJLNwu50i6gFwY9cHFr80BrQURYQBRkJ+Yv47nA3Fm7FeRhdWTiVTeqvSeOuMAOzBQ==", + "dev": true + }, + "@types/d3-contour": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-2.0.0.tgz", + "integrity": "sha512-PS9UO6zBQqwHXsocbpdzZFONgK1oRUgWtjjh/iz2vM06KaXLInLiKZ9e3OLBRerc1cU2uJYpO+8zOnb6frvCGQ==", + "dev": true, + "requires": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "@types/d3-delaunay": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-5.3.0.tgz", + "integrity": "sha512-gJYcGxLu0xDZPccbUe32OUpeaNtd1Lz0NYJtko6ZLMyG2euF4pBzrsQXms67LHZCDFzzszw+dMhSL/QAML3bXw==", + "dev": true + }, + "@types/d3-dispatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-2.0.0.tgz", + "integrity": "sha512-Sh0KW6z/d7uxssD7K4s4uCSzlEG/+SP+U47q098NVdOfFvUKNTvKAIV4XqjxsUuhE/854ARAREHOxkr9gQOCyg==", + "dev": true + }, + "@types/d3-drag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-2.0.0.tgz", + "integrity": "sha512-VaUJPjbMnDn02tcRqsHLRAX5VjcRIzCjBfeXTLGe6QjMn5JccB5Cz4ztMRXMJfkbC45ovgJFWuj6DHvWMX1thA==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-dsv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-2.0.0.tgz", + "integrity": "sha512-wYqy7T8tQ/DmocwxmlPujllLI5fg3lb6/FrVVWkLUD+NsRV+kcE4nbRZg10G9yjJ8pK2ZXqu+VP5jQbN13uNRQ==", + "dev": true + }, + "@types/d3-ease": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-2.0.0.tgz", + "integrity": "sha512-6aZrTyX5LG+ptofVHf+gTsThLRY1nhLotJjgY4drYqk1OkJMu2UvuoZRlPw2fffjRHeYepue3/fxTufqKKmvsA==", + "dev": true + }, + "@types/d3-fetch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-2.0.0.tgz", + "integrity": "sha512-WnLepGtxepFfXRdPI8I5FTgNiHn9p4vMTTqaNCzJJfAswXx0rOY2jjeolzEU063em3iJmGZ+U79InnEeFOrCRw==", + "dev": true, + "requires": { + "@types/d3-dsv": "*" + } + }, + "@types/d3-force": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-2.1.0.tgz", + "integrity": "sha512-LGDtC2YADu8OBniq9EBx/MOsXsMcJbEkmfSpXuz6oVdRamB+3CLCiq5EKFPEILGZQckkilGFq1ZTJ7kc289k+Q==", + "dev": true + }, + "@types/d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-uagdkftxnGkO4pZw5jEYOM5ZnZOEsh7z8j11Qxk85UkB2RzfUUxRl7R9VvvJZHwKn8l+x+rpS77Nusq7FkFmIg==", + "dev": true + }, + "@types/d3-geo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-2.0.0.tgz", + "integrity": "sha512-DHHgYXW36lnAEQMYU2udKVOxxljHrn2EdOINeSC9jWCAXwOnGn7A19B8sNsHqgpu4F7O2bSD7//cqBXD3W0Deg==", + "dev": true, + "requires": { + "@types/geojson": "*" + } + }, + "@types/d3-hierarchy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-2.0.0.tgz", + "integrity": "sha512-YxdskUvwzqggpnSnDQj4KVkicgjpkgXn/g/9M9iGsiToLS3nG6Ytjo1FoYhYVAAElV/fJBGVL3cQ9Hb7tcv+lw==", + "dev": true + }, + "@types/d3-interpolate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-2.0.0.tgz", + "integrity": "sha512-Wt1v2zTlEN8dSx8hhx6MoOhWQgTkz0Ukj7owAEIOF2QtI0e219paFX9rf/SLOr/UExWb1TcUzatU8zWwFby6gg==", + "dev": true, + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.0.tgz", + "integrity": "sha512-tXcR/9OtDdeCIsyl6eTNHC3XOAOdyc6ceF3QGBXOd9jTcK+ex/ecr00p9L9362e/op3UEPpxrToi1FHrtTSj7Q==", + "dev": true + }, + "@types/d3-polygon": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-2.0.0.tgz", + "integrity": "sha512-fISnMd8ePED1G4aa4V974Jmt+ajHSgPoxMa2D0ULxMybpx0Vw4WEzhQEaMIrL3hM8HVRcKTx669I+dTy/4PhAw==", + "dev": true + }, + "@types/d3-quadtree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-2.0.0.tgz", + "integrity": "sha512-YZuJuGBnijD0H+98xMJD4oZXgv/umPXy5deu3IimYTPGH3Kr8Th6iQUff0/6S80oNBD7KtOuIHwHUCymUiRoeQ==", + "dev": true + }, + "@types/d3-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-2.2.0.tgz", + "integrity": "sha512-Hjfj9m68NmYZzushzEG7etPvKH/nj9b9s9+qtkNG3/dbRBjQZQg1XS6nRuHJcCASTjxXlyXZnKu2gDxyQIIu9A==", + "dev": true + }, + "@types/d3-scale": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.2.1.tgz", + "integrity": "sha512-j+FryQSVk3GHLqjOX/RsHwGHg4XByJ0xIO1ASBTgzhE9o1tgeV4kEWLOzMzJRembKalflk5F03lEkM+4V6LDrQ==", + "dev": true, + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-scale-chromatic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-2.0.0.tgz", + "integrity": "sha512-Y62+2clOwZoKua84Ha0xU77w7lePiaBoTjXugT4l8Rd5LAk+Mn/ZDtrgs087a+B5uJ3jYUHHtKw5nuEzp0WBHw==", + "dev": true + }, + "@types/d3-selection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-2.0.0.tgz", + "integrity": "sha512-EF0lWZ4tg7oDFg4YQFlbOU3936e3a9UmoQ2IXlBy1+cv2c2Pv7knhKUzGlH5Hq2sF/KeDTH1amiRPey2rrLMQA==", + "dev": true + }, + "@types/d3-shape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.0.0.tgz", + "integrity": "sha512-NLzD02m5PiD1KLEDjLN+MtqEcFYn4ZL9+Rqc9ZwARK1cpKZXd91zBETbe6wpBB6Ia0D0VZbpmbW3+BsGPGnCpA==", + "dev": true, + "requires": { + "@types/d3-path": "^1" + }, + "dependencies": { + "@types/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==", + "dev": true + } + } + }, + "@types/d3-time": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.0.0.tgz", + "integrity": "sha512-Abz8bTzy8UWDeYs9pCa3D37i29EWDjNTjemdk0ei1ApYVNqulYlGUKip/jLOpogkPSsPz/GvZCYiC7MFlEk0iQ==", + "dev": true + }, + "@types/d3-time-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UpLg1mn/8PLyjr+J/JwdQJM/GzysMvv2CS8y+WYAL5K0+wbvXv/pPSLEfdNaprCZsGcXTxPsFMy8QtkYv9ueew==", + "dev": true + }, + "@types/d3-timer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-2.0.0.tgz", + "integrity": "sha512-l6stHr1VD1BWlW6u3pxrjLtJfpPZq9I3XmKIQtq7zHM/s6fwEtI1Yn6Sr5/jQTrUDCC5jkS6gWqlFGCDArDqNg==", + "dev": true + }, + "@types/d3-transition": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-2.0.0.tgz", + "integrity": "sha512-UJDzI98utcZQUJt3uIit/Ho0/eBIANzrWJrTmi4+TaKIyWL2iCu7ShP0o4QajCskhyjOA7C8+4CE3b1YirTzEQ==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-zoom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-2.0.0.tgz", + "integrity": "sha512-daL0PJm4yT0ISTGa7p2lHX0kvv9FO/IR1ooWbHR/7H4jpbaKiLux5FslyS/OvISPiJ5SXb4sOqYhO6fMB6hKRw==", + "dev": true, + "requires": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "@types/debug": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", + "dev": true + }, + "@types/eslint": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz", + "integrity": "sha512-I+1sYH+NPQ3/tVqCeUSBwTE/0heyvtXqpIopUUArlBm0Kpocb8FbMa3AZ/ASKIFpN3rnEx932TTXDbt9OXsNDw==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", + "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.45", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", + "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", + "dev": true + }, + "@types/express": { + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.9.tgz", + "integrity": "sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-rate-limit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-rate-limit/-/express-rate-limit-5.1.0.tgz", + "integrity": "sha512-vmg7S3hUnfFmp06V01DrTB41mbQYXMV/F4aF5KKnfCIeSlnizatXaqO9UgR6LvNEEd3eMpuUTLxR6nv3d4hZ6g==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.14.tgz", + "integrity": "sha512-uFTLwu94TfUFMToXNgRZikwPuZdOtDgs3syBtAIr/OXorL1kJqUJT9qCLnRZ5KBOWfZQikQ2xKgR2tnDj1OgDA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/fs-extra": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.5.tgz", + "integrity": "sha512-wr3t7wIW1c0A2BIJtdVp4EflriVaVVAsCAIHVzzh8B+GiFv9X1xeJjCs4upRXtzp7kQ6lP5xvskjoD4awJ1ZeA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/geoip-country": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/geoip-country/-/geoip-country-4.0.0.tgz", + "integrity": "sha512-RngLyEh1cMcH/fphQa4+AiMJX+t0/kD/CijkRCgZzQWwFE5ZnSP/WxVhcMAHfTY7fNgZvWtkgeKBc96dsEss9Q==", + "dev": true + }, + "@types/geojson": { + "version": "7946.0.7", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==", + "dev": true + }, + "@types/graceful-fs": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.4.tgz", + "integrity": "sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/history": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==", + "dev": true + }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "@types/html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==", + "dev": true + }, + "@types/http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==", + "dev": true + }, + "@types/http-proxy": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.4.tgz", + "integrity": "sha512-IrSHl2u6AWXduUaDLqYpt45tLVCtYv7o4Z0s1KghBCDgIIS9oW5K1H8mZG/A2CfeLdEa7rTd1ACOiHBc1EMT2Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "26.0.19", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.19.tgz", + "integrity": "sha512-jqHoirTG61fee6v6rwbnEuKhpSKih0tuhqeFbCmMmErhtu3BYlOZaXWjffgOstMM4S/3iQD31lI5bGLTrs97yQ==", + "dev": true, + "requires": { + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" + } + }, + "@types/js-cookie": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz", + "integrity": "sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, + "@types/json-stable-stringify": { + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.32.tgz", + "integrity": "sha512-q9Q6+eUEGwQkv4Sbst3J4PNgDOvpuVuKj79Hl/qnmBMEIPzB5QoFRUtjcgcg2xNUZyYUGXBk5wYIBKHt0A+Mxw==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/jsonwebtoken": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", + "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/lodash": { + "version": "4.14.165", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz", + "integrity": "sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg==", + "dev": true + }, + "@types/loud-rejection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/loud-rejection/-/loud-rejection-2.0.0.tgz", + "integrity": "sha512-oTHISsIybJGoh3b3Ay/10csbAd2k0su7G7DGrE1QWciC+IdydPm0WMw1+Gr9YMYjPiJ5poB3g5Ev73IlLoavLw==", + "dev": true, + "requires": { + "loud-rejection": "*" + } + }, + "@types/mime": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", + "dev": true + }, + "@types/minipass": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-2.2.0.tgz", + "integrity": "sha512-wuzZksN4w4kyfoOv/dlpov4NOunwutLA/q7uc00xU02ZyUY+aoM5PWIXEKBMnm0NHd4a+N71BMjq+x7+2Af1fg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/morgan": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.2.tgz", + "integrity": "sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/nedb": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/nedb/-/nedb-1.8.11.tgz", + "integrity": "sha512-qHQRLZ0e6l/XK/2Qb2v5N1ujmdttYkUvnRI4nPIifMy6vYwoAnER10xhX13isWjjQtNsrjNLinZgDDguzPmEKw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "12.19.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.9.tgz", + "integrity": "sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/overlayscrollbars": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@types/overlayscrollbars/-/overlayscrollbars-1.12.0.tgz", + "integrity": "sha512-h/pScHNKi4mb+TrJGDon8Yb06ujFG0mSg12wIO0sWMUF3dQIe2ExRRdNRviaNt9IjxIiOfnRr7FsQAdHwK4sMg==", + "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/passport": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.4.tgz", + "integrity": "sha512-h5OfAbfBBYSzjeU0GTuuqYEk9McTgWeGQql9g3gUw2/NNCfD7VgExVRYJVVeU13Twn202Mvk9BT0bUrl30sEgA==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/passport-jwt": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.3.tgz", + "integrity": "sha512-RlOCXiTitE8kazj9jZc6/BfGCSqnv2w/eYPDm3+3iNsquHn7ratu7oIUskZx9ZtnwMdpvdpy+Z/QYClocH5NvQ==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "@types/passport-strategy": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", + "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", + "dev": true, + "requires": { + "@types/express": "*", + "@types/passport": "*" + } + }, + "@types/prettier": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.1.5.tgz", + "integrity": "sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", + "dev": true + }, + "@types/qs": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", + "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/react": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", + "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dnd-multi-backend": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dnd-multi-backend/-/react-dnd-multi-backend-6.0.0.tgz", + "integrity": "sha512-QSU2mnwwNIMKCH6LqarZGgJo918O9I9bCCSnCZfNRcCn2Fz9R6XYhldUes9b3cxwpG08QEz0zHYOytTM8NizQA==", + "dev": true, + "requires": { + "@types/react": "*", + "react-dnd": "^10.0.2", + "react-dnd-touch-backend": "^10.0.2" + }, + "dependencies": { + "dnd-core": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-10.0.2.tgz", + "integrity": "sha512-PrxEjxF0+6Y1n1n1Z9hSWZ1tvnDXv9syL+BccV1r1RC08uWNsyetf8AnWmUF3NgYPwy0HKQJwTqGkZK+1NlaFA==", + "dev": true, + "requires": { + "@react-dnd/asap": "^4.0.0", + "@react-dnd/invariant": "^2.0.0", + "redux": "^4.0.4" + } + }, + "react-dnd": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-10.0.2.tgz", + "integrity": "sha512-SC2Ymvntynhoqtf5zaFhZscm9xenCoMofilxPdlwUlaelAzmbl9fw82C4ZJ//+lNm3kWAKXjGDZg2/aWjKEAtg==", + "dev": true, + "requires": { + "@react-dnd/shallowequal": "^2.0.0", + "@types/hoist-non-react-statics": "^3.3.1", + "dnd-core": "^10.0.2", + "hoist-non-react-statics": "^3.3.0" + } + }, + "react-dnd-touch-backend": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-10.0.2.tgz", + "integrity": "sha512-+lW/Ern0dKyHToD0oP+Wc/ZD6l7qJazosLqbjzL7OnPlig6WxdlrHkJylOLkeAdZj41fIJJ551Lb57pIL0CcPw==", + "dev": true, + "requires": { + "@react-dnd/invariant": "^2.0.0", + "dnd-core": "^10.0.2" + } + } + } + }, + "@types/react-dom": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz", + "integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-measure": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.6.tgz", + "integrity": "sha512-FxAwgDVKvxm4SPXu24x9cwzsty8x33UueazHcpxM1UWZlGJI57yIHM2djE3xUJhYVxuzNzi4E8UL3kmCkdh+4A==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-router": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz", + "integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.6.tgz", + "integrity": "sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-window": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.2.tgz", + "integrity": "sha512-gP1xam68Wc4ZTAee++zx6pTdDAH08rAkQrWm4B4F/y6hhmlT9Mgx2q8lTCXnrPHXsr15XjRN9+K2DLKcz44qEQ==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.8.tgz", + "integrity": "sha512-MoJhSQreaVoL+/hurAZzIm8wafFR6ajiTM1m4A0kv6AGeVBl4r4pOV8bGFrjjq1sGxDTnCoF8i22o0/aE5XCyA==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/spdy": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@types/spdy/-/spdy-3.4.4.tgz", + "integrity": "sha512-N9LBlbVRRYq6HgYpPkqQc3a9HJ/iEtVZToW6xlTtJiMhmRJ7jJdV7TaZQJw/Ve/1ePUsQiCTDc4JMuzzag94GA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", + "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", + "dev": true + }, + "@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, + "@types/superagent": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.10.tgz", + "integrity": "sha512-xAgkb2CMWUMCyVc/3+7iQfOEBE75NvuZeezvmixbUw3nmENf2tCnQkW5yQLTYqvXUQ+R6EXxdqKKbal2zM5V/g==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "@types/supertest": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.10.tgz", + "integrity": "sha512-Xt8TbEyZTnD5Xulw95GLMOkmjGICrOQyJ2jqgkSjAUR3mm7pAIzSR0NFBaMcwlzVvlpCjNwbATcWWwjNiZiFrQ==", + "dev": true, + "requires": { + "@types/superagent": "*" + } + }, + "@types/tar": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.4.tgz", + "integrity": "sha512-0Xv+xcmkTsOZdIF4yCnd7RkOOyfyqPaqJ7RZFKnwdxfDbkN3eAAE9sHl8zJFqBz4VhxolW9EErbjR1oyH7jK2A==", + "dev": true, + "requires": { + "@types/minipass": "*", + "@types/node": "*" + } + }, + "@types/yargs": { + "version": "15.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.11.tgz", + "integrity": "sha512-jfcNBxHFYJ4nPIacsi3woz1+kvUO6s1CyeEhtnDHBjHUMNj5UlW2GynmnSgiJJEdNg9yW5C8lfoNRZrHGv5EqA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.1.tgz", + "integrity": "sha512-QRLDSvIPeI1pz5tVuurD+cStNR4sle4avtHhxA+2uyixWGFjKzJ+EaFVRW6dA/jOgjV5DTAjOxboQkRDE8cRlQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.9.1", + "@typescript-eslint/scope-manager": "4.9.1", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.1.tgz", + "integrity": "sha512-c3k/xJqk0exLFs+cWSJxIjqLYwdHCuLWhnpnikmPQD2+NGAx9KjLYlBDcSI81EArh9FDYSL6dslAUSwILeWOxg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.9.1", + "@typescript-eslint/types": "4.9.1", + "@typescript-eslint/typescript-estree": "4.9.1", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.9.1.tgz", + "integrity": "sha512-Gv2VpqiomvQ2v4UL+dXlQcZ8zCX4eTkoIW+1aGVWT6yTO+6jbxsw7yQl2z2pPl/4B9qa5JXeIbhJpONKjXIy3g==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.9.1", + "@typescript-eslint/types": "4.9.1", + "@typescript-eslint/typescript-estree": "4.9.1", + "debug": "^4.1.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz", + "integrity": "sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.9.1", + "@typescript-eslint/visitor-keys": "4.9.1" + } + }, + "@typescript-eslint/types": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.1.tgz", + "integrity": "sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz", + "integrity": "sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.9.1", + "@typescript-eslint/visitor-keys": "4.9.1", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz", + "integrity": "sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.9.1", + "eslint-visitor-keys": "^2.0.0" + } + }, + "@vercel/ncc": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.25.1.tgz", + "integrity": "sha512-dGecC5+1wLof1MQpey4+6i2KZv4Sfs6WfXkl9KfO32GED4ZPiKxRfvtGPjbjZv0IbqMl6CxtcV1RotXYfd5SSA==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.1.tgz", + "integrity": "sha512-uMu1nCWn2Wxyy126LlGqRVlhdTOsO/bsBRI4dNq3+6SiSuRKRQX6ejjKgh82LoGAPSq72lDUiQ4FWVaf0PecYw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/wast-parser": "1.9.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.1.tgz", + "integrity": "sha512-5VEKu024RySmLKTTBl9q1eO/2K5jk9ZS+2HXDBLA9s9p5IjkaXxWiDb/+b7wSQp6FRdLaH1IVGIfOex58Na2pg==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.1.tgz", + "integrity": "sha512-y1lGmfm38djrScwpeL37rRR9f1D6sM8RhMpvM7CYLzOlHVboouZokXK/G88BpzW0NQBSvCCOnW5BFhten4FPfA==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.1.tgz", + "integrity": "sha512-uS6VSgieHbk/m4GSkMU5cqe/5TekdCzQso4revCIEQ3vpGZgqSSExi4jWpTWwDpAHOIAb1Jfrs0gUB9AA4n71w==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.1.tgz", + "integrity": "sha512-ZQ2ZT6Evk4DPIfD+92AraGYaFIqGm4U20e7FpXwl7WUo2Pn1mZ1v8VGH8i+Y++IQpxPbQo/UyG0Khs7eInskzA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.1" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.1.tgz", + "integrity": "sha512-J32HGpveEqqcKFS0YbgicB0zAlpfIxJa5MjxDxhu3i5ltPcVfY5EPvKQ1suRguFPehxiUs+/hfkwPEXom/l0lw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.1.tgz", + "integrity": "sha512-IEH2cMmEQKt7fqelLWB5e/cMdZXf2rST1JIrzWmf4XBt3QTxGdnnLvV4DYoN8pJjOx0VYXsWg+yF16MmJtolZg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.1.tgz", + "integrity": "sha512-i2rGTBqFUcSXxyjt2K4vm/3kkHwyzG6o427iCjcIKjOqpWH8SEem+xe82jUk1iydJO250/CvE5o7hzNAMZf0dQ==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.1.tgz", + "integrity": "sha512-FetqzjtXZr2d57IECK+aId3D0IcGweeM0CbAnJHkYJkcRTHP+YcMb7Wmc0j21h5UWBpwYGb9dSkK/93SRCTrGg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-buffer": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/wasm-gen": "1.9.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.1.tgz", + "integrity": "sha512-EvTG9M78zP1MmkBpUjGQHZc26DzPGZSLIPxYHCjQsBMo60Qy2W34qf8z0exRDtxBbRIoiKa5dFyWer/7r1aaSQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.1.tgz", + "integrity": "sha512-Oc04ub0vFfLnF+2/+ki3AE+anmW4sv9uNBqb+79fgTaPv6xJsOT0dhphNfL3FrME84CbX/D1T9XT8tjFo0IIiw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.1.tgz", + "integrity": "sha512-llkYtppagjCodFjo0alWOUhAkfOiQPQDIc5oA6C9sFAXz7vC9QhZf/f8ijQIX+A9ToM3c9Pq85X0EX7nx9gVhg==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.1.tgz", + "integrity": "sha512-S2IaD6+x9B2Xi8BCT0eGsrXXd8UxAh2LVJpg1ZMtHXnrDcsTtIX2bDjHi40Hio6Lc62dWHmKdvksI+MClCYbbw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-buffer": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/helper-wasm-section": "1.9.1", + "@webassemblyjs/wasm-gen": "1.9.1", + "@webassemblyjs/wasm-opt": "1.9.1", + "@webassemblyjs/wasm-parser": "1.9.1", + "@webassemblyjs/wast-printer": "1.9.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.1.tgz", + "integrity": "sha512-bqWI0S4lBQsEN5FTZ35vYzfKUJvtjNnBobB1agCALH30xNk1LToZ7Z8eiaR/Z5iVECTlBndoRQV3F6mbEqE/fg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/ieee754": "1.9.1", + "@webassemblyjs/leb128": "1.9.1", + "@webassemblyjs/utf8": "1.9.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.1.tgz", + "integrity": "sha512-gSf7I7YWVXZ5c6XqTEqkZjVs8K1kc1k57vsB6KBQscSagDNbAdxt6MwuJoMjsE1yWY1tsuL+pga268A6u+Fdkg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-buffer": "1.9.1", + "@webassemblyjs/wasm-gen": "1.9.1", + "@webassemblyjs/wasm-parser": "1.9.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.1.tgz", + "integrity": "sha512-ImM4N2T1MEIond0MyE3rXvStVxEmivQrDKf/ggfh5pP6EHu3lL/YTAoSrR7shrbKNPpeKpGesW1LIK/L4kqduw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-api-error": "1.9.1", + "@webassemblyjs/helper-wasm-bytecode": "1.9.1", + "@webassemblyjs/ieee754": "1.9.1", + "@webassemblyjs/leb128": "1.9.1", + "@webassemblyjs/utf8": "1.9.1" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.1.tgz", + "integrity": "sha512-2xVxejXSvj3ls/o2TR/zI6p28qsGupjHhnHL6URULQRcXmryn3w7G83jQMcT7PHqUfyle65fZtWLukfdLdE7qw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/floating-point-hex-parser": "1.9.1", + "@webassemblyjs/helper-api-error": "1.9.1", + "@webassemblyjs/helper-code-frame": "1.9.1", + "@webassemblyjs/helper-fsm": "1.9.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.1.tgz", + "integrity": "sha512-tDV8V15wm7mmbAH6XvQRU1X+oPGmeOzYsd6h7hlRLz6QpV4Ec/KKxM8OpLtFmQPLCreGxTp+HuxtH4pRIZyL9w==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/wast-parser": "1.9.1", + "@xtuc/long": "4.2.2" + } + }, + "@xobotyi/scrollbar-width": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", + "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==", + "dev": true + }, + "@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 + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "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-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, + "requires": {} + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "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" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "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" + } + }, + "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 + }, + "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" + } + } + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + } + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", + "dev": true + }, + "array-find": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz", + "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-includes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", + "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "get-intrinsic": "^1.0.1", + "is-string": "^1.0.5" + } + }, + "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 + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "array.prototype.flatmap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", + "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "function-bind": "^1.1.1" + } + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "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 + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==", + "dev": true + }, + "autoprefixer": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.1.0.tgz", + "integrity": "sha512-0/lBNwN+ZUnb5su18NZo5MBIjDaq6boQKZcxwy86Gip/CmXA2zZqUoFQLCNAGI5P25ZWSP2RWdhDJ8osfKEjoQ==", + "dev": true, + "requires": { + "browserslist": "^4.15.0", + "caniuse-lite": "^1.0.30001165", + "colorette": "^1.2.1", + "fraction.js": "^4.0.12", + "normalize-range": "^0.1.2", + "postcss-value-parser": "^4.1.0" + } + }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "dev": true, + "requires": { + "array-filter": "^1.0.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "axe-core": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.1.tgz", + "integrity": "sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==", + "dev": true + }, + "axios": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", + "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", + "dev": true, + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "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" + }, + "dependencies": { + "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 + } + } + }, + "babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "requires": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + } + }, + "babel-loader": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", + "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^1.4.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-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.0.tgz", + "integrity": "sha512-mGkvkpocWJes1CmMKtgGUwCeeq0pOhALyymozzDWYomHTbDLwueDYG6p4TK1YOeYHCzBzYPsWkgTto10JubI1Q==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + } + } + }, + "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 + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "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 + } + } + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bencode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bencode/-/bencode-2.0.1.tgz", + "integrity": "sha512-2uhEl8FdjSBUyb69qDTgOEeeqDTa+n3yMQzLW0cOzNf1Ow5bwcg3idf+qsWisIKRH8Bk8oC7UXL8irRcPA8ZEQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "binary-search-tree": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz", + "integrity": "sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=", + "dev": true, + "requires": { + "underscore": "~1.4.4" + } + }, + "block-stream2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.0.0.tgz", + "integrity": "sha512-1oI+RHHUEo64xomy1ozLgVJetFlHkIfQfJzTBQrj6xWnEMEPooeo2fZoqFjp0yzfHMBrgxwgh70tKp6T17+i3g==", + "dev": true, + "requires": { + "readable-stream": "^3.4.0" + } + }, + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "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.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "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 + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + }, + "dependencies": { + "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 + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "bowser": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz", + "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==", + "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==", + "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" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.15.0.tgz", + "integrity": "sha512-IJ1iysdMkGmjjYeRlDU8PQejVwxvVO5QOfXH7ylW31GO6LwNRSmm/SgRXtNsEXqMLl2e+2H5eEJ7sfynF8TCaQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001164", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.612", + "escalade": "^3.1.1", + "node-releases": "^1.1.67" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cacache": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz", + "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==", + "dev": true, + "requires": { + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "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.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "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.30001165", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz", + "integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "dependencies": { + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + } + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "clarinet": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/clarinet/-/clarinet-0.12.4.tgz", + "integrity": "sha512-Rx9Zw8KQkoPO3/O2yPRchCZm3cGubCQiRLmmFAlbkDKobUIPP3JYul+bKILR9DIv1gSVwPQSgF8JGGkXzX8Q0w==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==", + "dev": true + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "clipboard": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz", + "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==", + "dev": true, + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "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" + }, + "dependencies": { + "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 + }, + "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 + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "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" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + }, + "dependencies": { + "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 + } + } + }, + "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 + }, + "color-string": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "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": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "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.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "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=" + }, + "confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "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 + }, + "consola": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz", + "integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "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 + } + } + }, + "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.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "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 + } + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", + "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", + "dev": true, + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6" + } + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "dev": true, + "requires": { + "toggle-selection": "^1.0.6" + } + }, + "core-js-compat": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.1.tgz", + "integrity": "sha512-a16TLmy9NVD1rkjUGbwuyWkiDoN0FDpAwrfLONvHFQx0D9k7J9y0srwMT8QP/Z6HE3MIFaVynEeYwZwPX1o5RQ==", + "dev": true, + "requires": { + "browserslist": "^4.15.0", + "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-js-pure": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.1.tgz", + "integrity": "sha512-Se+LaxqXlVXGvmexKGPvnUIYC1jwXu1H6Pkyb3uBM5d8/NELMYCHs/4/roD7721NxrTLyv7e5nXd5/QLBO+10g==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "create-torrent": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/create-torrent/-/create-torrent-4.4.2.tgz", + "integrity": "sha512-FRxgYty6AF00xrYKMtpQ14ZJlst+i7mmUhcN4do7TTjktEntqAzfriaOIV6xk27t9GLTtraFnaTxsGgnyFA2eA==", + "dev": true, + "requires": { + "bencode": "^2.0.0", + "block-stream2": "^2.0.0", + "filestream": "^5.0.0", + "is-file": "^1.0.0", + "junk": "^3.1.0", + "minimist": "^1.1.0", + "multistream": "^4.0.0", + "once": "^1.3.0", + "piece-length": "^2.0.1", + "readable-stream": "^3.0.2", + "run-parallel": "^1.0.0", + "simple-sha1": "^3.0.0" + } + }, + "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" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "css-in-js-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", + "dev": true, + "requires": { + "hyphenate-style-name": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "css-loader": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.0.1.tgz", + "integrity": "sha512-cXc2ti9V234cq7rJzFKhirb2L2iPy8ZjALeVJAozXYz9te3r4eqLSixNAbMDJSgJEQywqXzs8gonxaboeKqwiw==", + "dev": true, + "requires": { + "camelcase": "^6.2.0", + "cssesc": "^3.0.0", + "icss-utils": "^5.0.0", + "loader-utils": "^2.0.0", + "postcss": "^8.1.4", + "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.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "css-minimizer-webpack-plugin": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-1.1.5.tgz", + "integrity": "sha512-mXgaoFjNpIudZfxD49N1aPtLxfXGJt+BVPVjQ+H66I48b5n4wJtFpYfffVr7izK8W6fD01J7K0kUcP6HGjw90w==", + "dev": true, + "requires": { + "cacache": "^15.0.5", + "cssnano": "^4.1.10", + "find-cache-dir": "^3.3.1", + "jest-worker": "^26.3.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "webpack-sources": "^1.4.3" + }, + "dependencies": { + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true + }, + "css-tree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.2.tgz", + "integrity": "sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "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": "4.1.10", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.7", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cssnano-preset-default": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", + "dev": true, + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.2", + "postcss-unique-selectors": "^4.0.1" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "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" + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "csstype": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "d3-array": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", + "integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==", + "dev": true + }, + "d3-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==", + "dev": true + }, + "d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==", + "dev": true + }, + "d3-interpolate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", + "dev": true, + "requires": { + "d3-color": "1 - 2" + } + }, + "d3-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz", + "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==", + "dev": true + }, + "d3-scale": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz", + "integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==", + "dev": true, + "requires": { + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "1 - 2", + "d3-time-format": "2 - 3" + } + }, + "d3-selection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz", + "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA==", + "dev": true + }, + "d3-shape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.0.0.tgz", + "integrity": "sha512-djpGlA779ua+rImicYyyjnOjeubyhql1Jyn1HK0bTyawuH76UQRWXd+pftr67H6Fa8hSwetkgb/0id3agKWykw==", + "dev": true, + "requires": { + "d3-path": "1 - 2" + } + }, + "d3-time": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz", + "integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q==", + "dev": true + }, + "d3-time-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", + "dev": true, + "requires": { + "d3-time": "1 - 2" + } + }, + "damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decimal.js": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz", + "integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "default-gateway": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.2.tgz", + "integrity": "sha512-bWrj9HZWNXJ/RUkWmBIp67JawNrPGz0il43IGWU84dazEYbNFQ52HbIiqgRQdYUHK3RyGrENrDV9QkwArt6IAQ==", + "dev": true, + "requires": { + "execa": "^4.0.3" + } + }, + "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" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dev": true, + "requires": { + "address": "^1.0.1", + "debug": "^2.6.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 + } + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "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" + } + }, + "dnd-core": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-11.1.3.tgz", + "integrity": "sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA==", + "dev": true, + "requires": { + "@react-dnd/asap": "^4.0.0", + "@react-dnd/invariant": "^2.0.0", + "redux": "^4.0.4" + } + }, + "dnd-multi-backend": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/dnd-multi-backend/-/dnd-multi-backend-6.0.0.tgz", + "integrity": "sha512-qfUO4V0IACs24xfE9m9OUnwIzoL+SWzSiFbKVIHE0pFddJeZ93BZOdHS1XEYr8X3HNh+CfnfjezXgOMgjvh74g==", + "dev": true + }, + "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": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "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-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", + "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "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" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "dev": true, + "requires": { + "xtend": "^4.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + } + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "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.3.619", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.619.tgz", + "integrity": "sha512-WFGatwtk7Fw0QcKCZzfGD72hvbcXV8kLY8aFuj0Ip0QRnOtyLYMsc+wXbSjb2w4lk1gcAeNU1/lQ20A+tvuypQ==", + "dev": true + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } + }, + "emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.0.tgz", + "integrity": "sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug==", + "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": "0.9.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", + "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.2.0", + "tapable": "^0.1.8" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "dev": true + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "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.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", + "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "dev": true, + "requires": { + "stackframe": "^1.1.1" + } + }, + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "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": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, - "array-unique": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "type-check": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz", + "integrity": "sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.2.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.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 }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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 + } + } + }, + "eslint-config-airbnb": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz", + "integrity": "sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^14.2.1", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + }, + "eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + }, + "eslint-config-airbnb-typescript": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-12.0.0.tgz", + "integrity": "sha512-TUCVru1Z09eKnVAX5i3XoNzjcCOU3nDQz2/jQGkg1jVYm+25fKClveziSl16celfCq+npU0MBPW/ZnXdGFZ9lw==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "4.4.1", + "eslint-config-airbnb": "18.2.0", + "eslint-config-airbnb-base": "14.2.0" + }, + "dependencies": { + "@typescript-eslint/parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.4.1.tgz", + "integrity": "sha512-S0fuX5lDku28Au9REYUsV+hdJpW/rNW0gWlc4SXzF/kdrRaAVX9YCxKpziH7djeWT/HFAjLZcnY7NJD8xTeUEg==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.4.1", + "@typescript-eslint/types": "4.4.1", + "@typescript-eslint/typescript-estree": "4.4.1", + "debug": "^4.1.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.4.1.tgz", + "integrity": "sha512-2oD/ZqD4Gj41UdFeWZxegH3cVEEH/Z6Bhr/XvwTtGv66737XkR4C9IqEkebCuqArqBJQSj4AgNHHiN1okzD/wQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.4.1", + "@typescript-eslint/visitor-keys": "4.4.1" + } + }, + "@typescript-eslint/types": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.4.1.tgz", + "integrity": "sha512-KNDfH2bCyax5db+KKIZT4rfA8rEk5N0EJ8P0T5AJjo5xrV26UAzaiqoJCxeaibqc0c/IvZxp7v2g3difn2Pn3w==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.4.1.tgz", + "integrity": "sha512-wP/V7ScKzgSdtcY1a0pZYBoCxrCstLrgRQ2O9MmCUZDtmgxCO/TCqOTGRVwpP4/2hVfqMz/Vw1ZYrG8cVxvN3g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.4.1", + "@typescript-eslint/visitor-keys": "4.4.1", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.4.1.tgz", + "integrity": "sha512-H2JMWhLaJNeaylSnMSQFEhT/S/FsJbebQALmoJxMPMxLtlVAMy2uJP/Z543n9IizhjRayLSqoInehCeNW9rWcw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.4.1", + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-config-airbnb": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.0.tgz", + "integrity": "sha512-Fz4JIUKkrhO0du2cg5opdyPKQXOI2MvF8KUvN2710nJMT6jaRUpRE2swrJftAjVGL7T1otLM5ieo5RqS1v9Udg==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^14.2.0", + "object.assign": "^4.1.0", + "object.entries": "^1.1.2" + } + }, + "eslint-config-airbnb-base": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz", + "integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.9", + "object.assign": "^4.1.0", + "object.entries": "^1.1.2" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "eslint-config-prettier": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.0.0.tgz", + "integrity": "sha512-8Y8lGLVPPZdaNA7JXqnvETVC7IiVRgAP6afQu9gOQRn90YY3otMNh+x7Vr2vMePQntF+5erdSUBqSzCmU/AxaQ==", + "dev": true, + "requires": {} + }, + "eslint-config-react-app": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz", + "integrity": "sha512-bpoAAC+YRfzq0dsTk+6v9aHm/uqnDwayNAXleMypGl6CpxI9oXXscVHo4fk3eJPIn+rsbtNetB4r/ZIidFIE8A==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "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 + } + } + }, + "eslint-import-resolver-webpack": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.0.tgz", + "integrity": "sha512-hZWGcmjaJZK/WSCYGI/y4+FMGQZT+cwW/1E/P4rDwFj2PbanlQHISViw4ccDJ+2wxAqjgwBfxwy3seABbVKDEw==", + "dev": true, + "requires": { + "array-find": "^1.0.0", + "debug": "^2.6.9", + "enhanced-resolve": "^0.9.1", + "find-root": "^1.1.0", + "has": "^1.0.3", + "interpret": "^1.2.0", + "lodash": "^4.17.15", + "node-libs-browser": "^1.0.0 || ^2.0.0", + "resolve": "^1.13.1", + "semver": "^5.7.1" + }, + "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 + } + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.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": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "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 + } + } + }, + "eslint-plugin-flowtype": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.2.0.tgz", + "integrity": "sha512-z7ULdTxuhlRJcEe1MVljePXricuPOrsWfScRXFhNzVD5dmTHWjIF57AxD0e7AbEoLSbjSsaA5S+hCg43WvpXJQ==", + "dev": true, + "requires": { + "lodash": "^4.17.15", + "string-natural-compare": "^3.0.1" + } + }, + "eslint-plugin-import": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -6840,226 +31577,351 @@ "ms": "2.0.0" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-jest": { + "version": "24.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.1.3.tgz", + "integrity": "sha512-dNGGjzuEzCE3d5EPZQ/QGtmlMotqnYWD/QpCZ1UuZlrMAdhG5rldh0N0haCvhGnUkSeuORS5VNROwF9Hrgn3Lg==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "^4.0.1" + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz", + "integrity": "sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.11.2", + "aria-query": "^4.2.2", + "array-includes": "^3.1.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.0.2", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.6", + "emoji-regex": "^9.0.0", + "has": "^1.0.3", + "jsx-ast-utils": "^3.1.0", + "language-tags": "^1.0.5" + } + }, + "eslint-plugin-react": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz", + "integrity": "sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flatmap": "^1.2.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "object.entries": "^1.1.2", + "object.fromentries": "^2.0.2", + "object.values": "^1.1.1", + "prop-types": "^15.7.2", + "resolve": "^1.18.1", + "string.prototype.matchall": "^4.0.2" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz", + "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==", + "dev": true, + "requires": {} + }, + "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-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "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 + } + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + }, + "eslint-webpack-plugin": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-2.4.1.tgz", + "integrity": "sha512-cj8iPWZKuAiVD8MMgTSunyMCAvxQxp5mxoPHZl1UMGkApFXaXJHdCFcCR+oZEJbBNhReNa5SjESIn34uqUbBtg==", + "dev": true, + "requires": { + "@types/eslint": "^7.2.4", + "arrify": "^2.0.1", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "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 + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "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.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "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 + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "exec-sh": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", + "dev": true + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "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" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "is-descriptor": "^0.1.0" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "is-extendable": "^0.1.0" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "kind-of": "^3.0.2" }, "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-buffer": "^1.1.5" } } } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "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-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { "kind-of": "^3.0.2" @@ -7076,37 +31938,96 @@ } } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "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.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "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": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "ms": "2.0.0" } }, "ms": { @@ -7114,13 +32035,116 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true + }, + "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 + } + } + }, + "express-rate-limit": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.2.3.tgz", + "integrity": "sha512-cjQH+oDrEPXxc569XvxhHC6QXqJiuBT6BhZ70X3bdAImcnHnTNMVuMAJaT0TXPoRiEErUrVPRcOTpZpM36VbOQ==", + "dev": true + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true } } }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "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.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fast-json-patch": { + "version": "3.0.0-1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.0.0-1.tgz", + "integrity": "sha512-6pdFb07cknxvPzCeLsFHStEy+MysPJPgZQ9LbQ/2O67unQF93SNqfdSqnPPl71YMHX+AD8gbl7iuoGFzHEdDuw==", + "dev": true + }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "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 }, "fast-levenshtein": { "version": "2.0.6", @@ -7128,10 +32152,44 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" + "fast-memoize": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", + "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", + "dev": true + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, + "fast-shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz", + "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==", + "dev": true + }, + "fast-sort": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-2.2.0.tgz", + "integrity": "sha512-W7zqnn2zsYoQA87FKmYtgOsbJohOrh7XrtZrCVHN5XZKqTBTv5UG+rSS3+iWbg/nepRQUOu+wnas8BwtK8kiCg==", + "dev": true + }, + "fastest-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz", + "integrity": "sha1-kSLUBtTJ2YvqZEpraFPVh0uHsCg=", + "dev": true + }, + "fastq": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz", + "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } }, "faye-websocket": { "version": "0.11.3", @@ -7143,49 +32201,12 @@ } }, "fb-watchman": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", "dev": true, "requires": { - "bser": "^2.0.0" - } - }, - "fbemitter": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-2.1.1.tgz", - "integrity": "sha1-Uj4U/a9SSIBbsC9i78M75wP1GGU=", - "requires": { - "fbjs": "^0.8.4" - } - }, - "fbjs": { - "version": "0.8.17", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", - "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", - "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" - }, - "dependencies": { - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - } + "bser": "2.1.1" } }, "fd-slicer": { @@ -7197,96 +32218,126 @@ } }, "feedme": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/feedme/-/feedme-1.2.0.tgz", - "integrity": "sha512-GNaewCsb6eWTgWqvxnGCYm6MkYrRSlXyqNhJRbdX7ku2Ubks3Vlg0cveea+gnK3mYh4pjfMQpzWpYyFl2RvILw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/feedme/-/feedme-2.0.2.tgz", + "integrity": "sha512-0RNn0uLaSey8EThxgABR0T1Q47kSRatYnAXy1cfUc8/eypqXiAu38XGthuwwzb7mESCD9465k6Nym8D9AtB8HA==", + "dev": true, "requires": { - "clarinet": "^0.12.0", - "eventyoshi": "^0.2.0", - "sax": "^1.0.0" + "clarinet": "^0.12.4", + "sax": "^1.2.4" } }, "feedsub": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/feedsub/-/feedsub-0.6.0.tgz", - "integrity": "sha512-mngTFKH9foXJGc3hcw81P/E2EygXkI0Ie224hE4bd5kNxkru1LYj7QjQErwNgUotfyGjn1vxNDeC4S5v4jMzLg==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/feedsub/-/feedsub-0.7.6.tgz", + "integrity": "sha512-6M8XMKPePkntE+ZRrnY/U/JVV7BLUuNHiSUfnTu8ArLWWi+CuRsqF5g4TbEruJLJLvt3Fkgj/o14o66xHMTT7g==", + "dev": true, "requires": { - "feedme": "^1.0.0", - "miniget": "^1.3.0", - "newsemitter": "^0.3.0" + "feedme": "^2.0.2", + "miniget": "^4.0.0", + "newsemitter": "^1.0.2", + "tiny-typed-emitter": "^2.0.3" } }, - "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" - }, "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "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 + } } }, "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", + "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", "dev": true, "requires": { - "flat-cache": "^2.0.1" + "flat-cache": "^3.0.4" } }, "file-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz", - "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, "requires": { - "loader-utils": "^1.2.3", - "schema-utils": "^2.5.0" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } } }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "file-selector": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz", + "integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==", "dev": true, "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" + "tslib": "^2.0.3" } }, "filesize": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", - "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", + "integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==", "dev": true }, + "filestream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/filestream/-/filestream-5.0.0.tgz", + "integrity": "sha512-5H3RqSaJp12THfZiNWodYM7TiKfQvrpX+EIOrB1XvCceTys4yvfEIl8wDp+/yI8qj6Bxym8m0NYWwVXDAet/+A==", + "dev": true, + "requires": { + "readable-stream": "^3.4.0", + "typedarray-to-buffer": "^3.0.0" + } + }, "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "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": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "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", @@ -7301,6 +32352,7 @@ "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" } @@ -7308,18 +32360,31 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, "requires": { "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "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" + } + } } }, "find-root": { @@ -7329,36 +32394,78 @@ "dev": true }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "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": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", + "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "dev": true + }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "fork-ts-checker-webpack-plugin": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz", + "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "chalk": "^2.4.1", + "micromatch": "^3.1.10", + "minimatch": "^3.0.4", + "semver": "^5.6.0", + "tapable": "^1.0.0", + "worker-rpc": "^0.1.0" }, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "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" + } }, "braces": { "version": "2.3.2", @@ -7389,142 +32496,37 @@ } } }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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": { - "ms": "2.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "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": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "color-name": "1.1.3" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "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 }, "fill-range": { "version": "4.0.0", @@ -7533,65 +32535,33 @@ "dev": true, "requires": { "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true }, "is-number": { "version": "3.0.0", @@ -7613,18 +32583,6 @@ } } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -7646,881 +32604,662 @@ "to-regex": "^3.0.2" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "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" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } } } }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "dev": true + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fraction.js": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz", + "integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "dev": true, + "requires": { + "js-yaml": "^3.13.1" + } + }, + "frontmatter-markdown-loader": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/frontmatter-markdown-loader/-/frontmatter-markdown-loader-3.6.2.tgz", + "integrity": "sha512-yZ4rQOIHgtGskRlUPsdJ5+Lo23mzyAdmFdN7JiXBzgfjrKIpOHnejmHw7EItHVRjlTwFNE1L7md4k8wmByJ33Q==", "dev": true, "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "front-matter": "^4.0.0", + "loader-utils": "^2.0.0", + "markdown-it": "^11.0.0" }, "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", "dev": true, "requires": { - "glob": "^7.1.3" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } } } }, - "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz", + "integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", + "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", + "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 }, - "flood-ui-kit": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/flood-ui-kit/-/flood-ui-kit-0.1.11.tgz", - "integrity": "sha512-mUEna1knrqnFa4bUnXzevvxjMBOgjidO+36uQ1XB4TpIKNdmTjdwbyoIkBhYySPCXgbq1fPdvmg8RVOFjTQS1Q==", + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, "requires": { - "classnames": "^2.2.6", - "normalize.css": "^7.0.0", - "prop-types": "^15.7.2", - "react": "^16.12.0", - "react-dom": "^16.12.0", - "react-router": "^5.1.2", - "react-router-dom": "^5.1.2", - "react-transition-group": "^4.3.0" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" }, "dependencies": { - "hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { - "react-is": "^16.7.0" + "ansi-regex": "^2.0.0" } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + } + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "requires": { + "globule": "^1.0.0" + } + }, + "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 + }, + "geoip-country": { + "version": "4.0.46", + "resolved": "https://registry.npmjs.org/geoip-country/-/geoip-country-4.0.46.tgz", + "integrity": "sha512-hkgLJ4mUsJSZBtM0zcqnAMrui73hRwvUozm2F87Q1gH7oPGB33IC8GsDW7nigKDye/00Y7kQsq1g/SIiG8WLuw==", + "requires": { + "async": "^2.6.1", + "colors": "^1.4.0", + "glob": "^7.1.6", + "iconv-lite": "^0.5.2", + "ip-address": "^6.3.0", + "lazy": "^1.0.11", + "rimraf": "^2.7.1", + "yauzl": "^2.10.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", "requires": { - "isarray": "0.0.1" + "safer-buffer": ">= 2.1.2 < 3" } }, - "react-router": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz", - "integrity": "sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==", - "requires": { - "@babel/runtime": "^7.1.2", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.3.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" } } } }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "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.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" } }, - "flux": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/flux/-/flux-3.1.3.tgz", - "integrity": "sha1-0jvtUVp5oi2TOrU6tK2hnQWy8Io=", + "get-node-dimensions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz", + "integrity": "sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ==", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, "requires": { - "fbemitter": "^2.0.0", - "fbjs": "^0.8.0" + "pump": "^3.0.0" } }, - "follow-redirects": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", - "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", + "get-user-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-1.4.0.tgz", + "integrity": "sha512-gQo03lP1OArHLKlnoglqrGGl7b04u2EP9Xutmp72cMdtrrSD7ZgIsCsUKZynYWLDkVJW33Cj3pliP7uP0UonHQ==", + "dev": true, + "requires": { + "lodash.once": "^4.1.1" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "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 + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", "dev": true, "requires": { - "debug": "^3.0.0" + "global-prefix": "^3.0.0" } }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", "dev": true, "requires": { - "for-in": "^1.0.1" + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "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" + } + } } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "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 }, - "fork-ts-checker-webpack-plugin": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.5.0.tgz", - "integrity": "sha512-zEhg7Hz+KhZlBhILYpXy+Beu96gwvkROWJiTXOCyOOMMrdBIRPvsBpBqgTI4jfJGrJXcqGwJR8zsBGDmzY0jsA==", + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", - "chalk": "^2.4.1", - "chokidar": "^2.0.4", - "micromatch": "^3.1.10", - "minimatch": "^3.0.4", - "semver": "^5.6.0", - "tapable": "^1.0.0", - "worker-rpc": "^0.1.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" }, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "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" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true } } }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "globule": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", + "dev": true, "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" } }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "dev": true, "requires": { - "map-cache": "^0.2.2" + "delegate": "^3.1.2" } }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true, + "optional": true + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "duplexer": "^0.1.1", + "pify": "^4.0.1" } }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + "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 }, - "fs-extra": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", - "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "ajv": "^6.12.3", + "har-schema": "^2.0.0" } }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "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": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" + "function-bind": "^1.1.1" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz", - "integrity": "sha512-tPvHgPGB7m40CZ68xqFGkKuzN+RnpGmSV+hgeKxhRpbxdqKXUFJGC3yonBOLzQBcJyGpdZFDfCsdOC2KFsXzeA==", - "optional": true, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" + "ansi-regex": "^2.0.0" }, "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "optional": 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" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "bundled": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "optional": true, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "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 + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "kind-of": "^3.0.2" }, "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } } } }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash-wasm": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.4.1.tgz", + "integrity": "sha512-6hbcJY74kQQOAoDQZgm0rku51jlf0f3+g+q+1CI3vNvabF27SQuVJm86FnMNMaw8WEjBeW4U5GWX1KsKd8qZCw==", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "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 + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dev": true, + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "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.6", - "bundled": true, - "optional": true, + "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", @@ -8531,4946 +33270,6385 @@ "util-deprecate": "~1.0.1" } }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "string_decoder": { "version": "1.1.1", - "bundled": true, - "optional": true, + "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" } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "optional": true, + } + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "dev": true, + "requires": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + } + } + }, + "html-webpack-plugin": { + "version": "5.0.0-alpha.15", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.0.0-alpha.15.tgz", + "integrity": "sha512-SFnpxiOo8EZ37fPUG4elnii78E7WBlv6pqJl0rwK5knWRpvzQbbPGmn/hNrnBvgPbKi5RHwoaEdsQkLvHk+AzA==", + "dev": true, + "requires": { + "@types/html-minifier-terser": "^5.0.0", + "html-minifier-terser": "^5.0.1", + "loader-utils": "2.0.0", + "lodash": "^4.17.20", + "pretty-error": "^2.1.1", + "tapable": "2.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, "requires": { - "string-width": "^1.0.2 || 2" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "optional": true + "tapable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.0.0.tgz", + "integrity": "sha512-bjzn0C0RWoffnNdTzNi7rNDhs1Zlwk2tRXgk8EiHKAOX1Mag3d6T0Y5zNa7l9CJ+EoUne/0UHdwS8tMbkh9zDg==", + "dev": true + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + } + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true } } }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "http-parser-js": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz", + "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", + "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": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" } }, - "function-bind": { + "http-proxy-middleware": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.0.6.tgz", + "integrity": "sha512-NyL6ZB6cVni7pl+/IT2W0ni5ME00xR0sN27AQZZrpKn1b+qRh+mLbBxIq9Cq1oGfmTc7BUq4HB77mxwCaxAYNg==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.4", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "lodash": "^4.17.20", + "micromatch": "^4.0.2" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "human-signals": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", "dev": true }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "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": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "safer-buffer": ">= 2.1.2 < 3" } }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "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, + "requires": {} + }, + "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": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "immer": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/immer/-/immer-7.0.9.tgz", + "integrity": "sha512-Vs/gxoM4DqNAYR7pugIxi0Xc8XAun/uy7AQu4fLLqaTBHxjOP9pJ266Q9MWA/ly4z6rAFZbvViOtihxUZ7O28A==", + "dev": true + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, "requires": { - "globule": "^1.0.0" + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" } }, - "geoip-country": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/geoip-country/-/geoip-country-3.3.1.tgz", - "integrity": "sha512-qn4rpbmD1ow2FFW/zkKm2Y2qN3L9z0J3/7thTZtxuBpA6+tV3ZiD5gClvGogpQteHizZZ+pypJ/CgKFomb/cxQ==", + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, "requires": { - "async": "^2.6.1", - "colors": "^1.4.0", - "glob": "^7.1.5", - "iconv-lite": "^0.5.0", - "ip-address": "^6.1.0", - "lazy": "^1.0.11", - "rimraf": "^2.7.1", - "yauzl": "^2.10.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" }, "dependencies": { - "iconv-lite": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz", - "integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==", + "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": { - "safer-buffer": ">= 2.1.2 < 3" + "find-up": "^4.0.0" } } } }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { - "assert-plus": "^1.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "inline-style-prefixer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-4.0.2.tgz", + "integrity": "sha512-N8nVhwfYga9MiV9jWlwfdj1UDIaZlBFu4cJSJkIr7tZX7sHpHhGR5su1qdpW+7KPL8ISTvCIkcaFi/JdBknvPg==", + "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" + "bowser": "^1.7.3", + "css-in-js-utils": "^2.0.0" } }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "internal-ip": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", + "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", "dev": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "default-gateway": "^6.0.0", + "ipaddr.js": "^1.9.1", + "is-ip": "^3.1.0", + "p-event": "^4.2.0" } }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "internal-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz", + "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==", "dev": true, "requires": { - "is-glob": "^2.0.0" + "es-abstract": "^1.17.0-next.1", + "has": "^1.0.3", + "side-channel": "^1.0.2" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } } }, - "glob-to-regexp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", - "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "intl-messageformat": { + "version": "9.3.20", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.3.20.tgz", + "integrity": "sha512-jmpjYHE076J/0CIofrPhtUC4LfmsAhuv4JMQxytl2KJd2bim+3+gQJh+Z1vyHUzcj4fIHdt388ZGchb8f0NwOA==", "dev": true, "requires": { - "ini": "^1.3.4" + "fast-memoize": "^2.5.2", + "intl-messageformat-parser": "6.0.18", + "tslib": "^2.0.1" } }, - "global-modules": { + "intl-messageformat-parser": { + "version": "6.0.18", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.0.18.tgz", + "integrity": "sha512-vLjACEunfi5uSUCWFLOR4PXQ9DGLpED3tM7o9zsYsOvjl0VIheoxyG0WZXnsnhn+S+Zu158M6CkuHXeNZfKRRg==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "1.5.0", + "tslib": "^2.0.1" + } + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-address": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-6.4.0.tgz", + "integrity": "sha512-c5uxc2WUTuRBVHT/6r4m7HIr/DfV0bF6DvLH3iZGSK8wp8iMwwZSgIq2do0asFf8q9ECug0SE+6+1ACMe4sorA==", + "requires": { + "jsbn": "1.1.0", + "lodash.find": "4.6.0", + "lodash.max": "4.0.1", + "lodash.merge": "4.6.2", + "lodash.padstart": "4.6.1", + "lodash.repeat": "4.1.0", + "sprintf-js": "1.1.2" + } + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "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 + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, + "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-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-ci": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, "requires": { - "global-prefix": "^3.0.0" + "ci-info": "^2.0.0" } }, - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", "dev": true, "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" } }, - "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==" - }, - "globby": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", - "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", "dev": true, "requires": { - "array-union": "^1.0.1", - "dir-glob": "2.0.0", - "fast-glob": "^2.0.2", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } + "has": "^1.0.3" } }, - "globule": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" + "kind-of": "^6.0.0" } }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "requires": { - "delegate": "^3.1.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-docker": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", + "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "dev": true + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "is-plain-object": "^2.0.4" } }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "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 }, - "gud": { + "is-file": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", - "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + "resolved": "https://registry.npmjs.org/is-file/-/is-file-1.0.0.tgz", + "integrity": "sha1-KKRM+9nT2xkwRfIrZfzo7fliBZY=", + "dev": true }, - "gzip-size": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", - "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "duplexer": "^0.1.1", - "pify": "^4.0.1" + "number-is-nan": "^1.0.0" } }, - "handle-thing": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=" + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true }, - "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "is-generator-function": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz", + "integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" + "is-extglob": "^2.1.1" + } + }, + "is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dev": true, + "requires": { + "ip-regex": "^4.0.0" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "ip-regex": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.2.0.tgz", + "integrity": "sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A==", "dev": true } } }, - "har-schema": { + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "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-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "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==" - } - } + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "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": { - "function-bind": "^1.1.1" + "isobject": "^3.0.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "is-potential-custom-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", + "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "has-symbols": "^1.0.1" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "dev": true }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } + "html-comment-regex": "^1.1.0" } }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "has-symbols": "^1.0.1" } }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "is-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.4.tgz", + "integrity": "sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA==", "dev": true, "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" } }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, - "history": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz", - "integrity": "sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==", - "requires": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^2.2.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^0.4.0" - } + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "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": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "is-docker": "^2.0.0" } }, - "hoek": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", - "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" - }, - "hoist-non-react-statics": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, - "home-or-tmp": { + "isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "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 + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", "dev": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "parse-passwd": "^1.0.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" } }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", "dev": true, "requires": { - "whatwg-encoding": "^1.0.1" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "html-minifier": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", - "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, "requires": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.2.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" - }, - "dependencies": { - "commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" - } + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" } }, - "html-minifier-terser": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.0.2.tgz", - "integrity": "sha512-VAaitmbBuHaPKv9bj47XKypRhgDxT/cDLvsPiiF7w+omrN3K0eQhpigV9Z1ilrmHa9e0rOYcD6R/+LCDADGcnQ==", + "jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, "requires": { - "camel-case": "^3.0.0", - "clean-css": "^4.2.1", - "commander": "^4.0.0", - "he": "^1.2.0", - "param-case": "^2.1.1", - "relateurl": "^0.2.7", - "terser": "^4.3.9" - }, - "dependencies": { - "commander": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.0.1.tgz", - "integrity": "sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA==" - } + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" } }, - "html-to-react": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/html-to-react/-/html-to-react-1.4.2.tgz", - "integrity": "sha512-TdTfxd95sRCo6QL8admCkE7mvNNrXtGoVr1dyS+7uvc8XCqAymnf/6ckclvnVbQNUo2Nh21VPwtfEHd0khiV7g==", + "jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, "requires": { - "domhandler": "^3.0", - "htmlparser2": "^4.0", - "lodash.camelcase": "^4.3.0", - "ramda": "^0.26" + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" }, "dependencies": { - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + "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 }, - "domhandler": { + "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz", - "integrity": "sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==", + "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 + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, "requires": { - "domelementtype": "^2.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "domutils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.0.0.tgz", - "integrity": "sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg==", + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "requires": { - "dom-serializer": "^0.2.1", - "domelementtype": "^2.0.1", - "domhandler": "^3.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, - "entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true }, - "htmlparser2": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.0.0.tgz", - "integrity": "sha512-cChwXn5Vam57fyXajDtPXL1wTYc8JtLbr2TN76FYu05itVVVealxLowe2B3IEznJG4p9HAYn/0tJaRlGuEglFQ==", + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^3.0.0", - "domutils": "^2.0.0", - "entities": "^2.0.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } }, - "html-webpack-plugin": { - "version": "4.0.0-beta.11", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz", - "integrity": "sha512-4Xzepf0qWxf8CGg7/WQM5qBB2Lc/NFI7MhU59eUDTkuQp3skZczH4UA1d6oQyDEIoMDgERVhRyTdtUPZ5s5HBg==", + "jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, "requires": { - "html-minifier-terser": "^5.0.1", - "loader-utils": "^1.2.3", - "lodash": "^4.17.15", - "pretty-error": "^2.1.1", - "tapable": "^1.1.3", - "util.promisify": "1.0.0" + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" } }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" } }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + "jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" } }, - "http-parser-js": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", - "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", + "jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + } + }, + "jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "requires": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + } + }, + "jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, - "http-proxy": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", "dev": true, "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" } }, - "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + } + }, + "jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", "dev": true, "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + } + }, + "jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + } + }, + "jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true + }, + "jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", "dev": true, "requires": { - "ms": "2.0.0" + "@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" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "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": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "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 } } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "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": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } + "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 + } + } + }, + "jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + } + }, + "jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + } + }, + "jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "requires": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "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 + }, + "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 + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" } }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + } + } + }, + "jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + } + }, + "jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "lru-cache": "^6.0.0" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" } }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + } + }, + "jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "requires": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", "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==", + "js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", + "dev": true + }, + "js-file-download": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.12.tgz", + "integrity": "sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==", + "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 + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, - "icss-replace-symbols": { + "jsbn": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=" }, - "icss-utils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", - "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", - "requires": { - "postcss": "^7.0.14" - }, - "dependencies": { - "postcss": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.23.tgz", - "integrity": "sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } + "jsdom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.4.0.tgz", + "integrity": "sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "acorn": "^7.1.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.2.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.0", + "domexception": "^2.0.1", + "escodegen": "^1.14.1", + "html-encoding-sniffer": "^2.0.1", + "is-potential-custom-element-name": "^1.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.1", + "request": "^2.88.2", + "request-promise-native": "^1.0.8", + "saxes": "^5.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0", + "ws": "^7.2.3", + "xml-name-validator": "^3.0.0" } }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "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 }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "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 }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=" - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true }, - "immer": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/immer/-/immer-1.10.0.tgz", - "integrity": "sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==", + "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 }, - "import-fresh": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", - "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "jsonify": "~0.0.0" } }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "import-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", - "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { - "pkg-dir": "^2.0.0", - "resolve-cwd": "^2.0.0" + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" }, "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { + "universalify": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } } } }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true }, - "in-publish": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dev": true, + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + } }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, "requires": { - "repeating": "^2.0.0" + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" } }, - "indexes-of": { + "jsx-ast-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz", + "integrity": "sha512-d4/UOjg+mxAWxCiF0c5UTSwyqbchkbqCvK87aBovhnh8GtysTjWmgC63tY0cJx/HzGgm9qnA147jVBdpOiQ2RA==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "object.assign": "^4.1.1" + } + }, + "junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "killable": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "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 }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "klona": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", + "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", + "dev": true + }, + "language-subtag-registry": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", + "dev": true + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", + "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "language-subtag-registry": "~0.3.2" } }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "lazy": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha1-2qBoIGKCVCwIgojpdcKXwa53tpA=" + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "dev": true }, - "inquirer": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.4.1.tgz", - "integrity": "sha512-/Jw+qPZx4EDYsaT6uz7F4GJRNFMRdKNeUZw3ZnKV8lyuUgz/YWRCSUAJMZSVhSq4Ec0R2oYnyi6b3d4JXcL5Nw==", + "linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==", "dev": true, "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.11", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" + "uc.micro": "^1.0.1" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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 - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "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" - } - } + "error-ex": "^1.2.0" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "loader-runner": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.1.0.tgz", + "integrity": "sha512-oR4lB4WvwFoC70ocraKhn5nkKSs23t57h9udUgw8o0iH8hMXeEoRuUgfcvgUwAJ1ZpRqBvcou4N2SMvM1DwMrA==", + "dev": true + }, + "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" + }, + "dependencies": { + "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": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } + "minimist": "^1.2.0" } } } }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "localforage": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz", + "integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==", "dev": true, "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" + "lie": "3.1.1" } }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true - }, - "intl-format-cache": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.2.9.tgz", - "integrity": "sha512-Zv/u8wRpekckv0cLkwpVdABYST4hZNTDaX7reFetrYTJwxExR2VyTqQm+l0WmL0Qo8Mjb9Tf33qnfj0T7pjxdQ==" - }, - "intl-messageformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz", - "integrity": "sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw=", + "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": { - "intl-messageformat-parser": "1.4.0" + "p-locate": "^4.1.0" } }, - "intl-messageformat-parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz", - "integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU=" - }, - "intl-relativeformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.2.0.tgz", - "integrity": "sha512-4bV/7kSKaPEmu6ArxXf9xjv1ny74Zkwuey8Pm01NH4zggPP7JHwg2STk8Y3JdspCKRDriwIyLRfEXnj2ZLr4Bw==", - "requires": { - "intl-messageformat": "^2.0.0" - } + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } + "lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=", + "dev": true }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=", "dev": true }, - "ip-address": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-6.1.0.tgz", - "integrity": "sha512-u9YYtb1p2fWSbzpKmZ/b3QXWA+diRYPxc2c4y5lFB/MMk5WZ7wNZv8S3CFcIGVJ5XtlaCAl/FQy/D3eQ2XtdOA==", - "requires": { - "jsbn": "1.1.0", - "lodash": "^4.17.15", - "sprintf-js": "1.1.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" - } - } + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=", + "dev": true }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=", "dev": true }, - "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true }, - "is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", "dev": true }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - } + "lodash.max": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.max/-/lodash.max-4.0.1.tgz", + "integrity": "sha1-hzVWbGGLNan3YFILSHrnllivE2o=" }, - "is-alphabetical": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.3.tgz", - "integrity": "sha512-eEMa6MKpHFzw38eKm56iNNi6GJ7lf6aLLio7Kr23sJPAECscgRtZvOBYybejWDQ2bM949Y++61PY+udzj5QMLA==" + "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 }, - "is-alphanumerical": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.3.tgz", - "integrity": "sha512-A1IGAPO5AW9vSh7omxIlOGwIqEvpW/TA+DksVOPM5ODuxKlZS09+TEM1E3275lJqO2oJ38vDpeAL3DCIiHE6eA==", - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "requires": { - "binary-extensions": "^1.0.0" - } + "lodash.repeat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz", + "integrity": "sha1-/H3oEx2MisB+S0n3T/6CnR8r7EQ=" }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true }, - "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "requires": { - "ci-info": "^1.5.0" + "js-tokens": "^3.0.0 || ^4.0.0" } }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "loud-rejection": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-2.2.0.tgz", + "integrity": "sha512-S0FayMXku80toa5sZ6Ro4C+s+EtFDCsyJNG/AzFMfX3AxD5Si4dZsgzm/kKnbOxHl5Cv8jBlno8+3XYIh2pNjQ==", + "dev": true, "requires": { - "kind-of": "^3.0.2" + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.2" } }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-decimal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.3.tgz", - "integrity": "sha512-bvLSwoDg2q6Gf+E2LEPiklHZxxiSi3XAh4Mav65mKqTfCO1HM3uBs24TjEH8iJX3bbDdLXKJXBTmGzuTUuAEjQ==" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "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": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } + "tslib": "^2.0.3" } }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "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": { - "is-primitive": "^2.0.0" + "yallist": "^4.0.0" } }, - "is-expression": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", - "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", + "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": { - "acorn": "~4.0.2", - "object-assign": "^4.0.1" + "semver": "^6.0.0" }, "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "tmpl": "1.0.x" } }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "p-defer": "^1.0.0" } }, - "is-generator-fn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-1.0.0.tgz", - "integrity": "sha1-lp1J4bszKfa7fwkIm+JleLLd1Go=", + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "object-visit": "^1.0.0" } }, - "is-hexadecimal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.3.tgz", - "integrity": "sha512-zxQ9//Q3D/34poZf8fiy3m3XVpbQc7ren15iKqrTtLPwkPD/t3Scy9Imp63FujULGxuK0ZlCwoo5xNpktFgbOA==" - }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "markdown-it": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.1.tgz", + "integrity": "sha512-aU1TzmBKcWNNYvH9pjq6u92BML+Hz3h5S/QpfTFwiQF852pLT+9qHsrhM9JYipkOXZxGn+sGH8oyJE9FD9WezQ==", "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" } }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", - "dev": true - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { - "kind-of": "^3.0.2" + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "is-obj": { + "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 + }, + "mdurl": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", "dev": true }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "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 }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "mem": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-8.0.0.tgz", + "integrity": "sha512-qrcJOe6uD+EW8Wrci1Vdiua/15Xw3n/QnaNXE7varnB6InxSk7nu3/i5jfy3S6kWxr8WYJ6R1o0afMUtvorTsA==", "dev": true, "requires": { - "is-path-inside": "^2.1.0" + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^3.1.0" }, "dependencies": { - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true } } }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "memfs": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz", + "integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "fs-monkey": "1.0.1" } }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==", + "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==", + "memory-fs": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", + "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, "requires": { - "isobject": "^3.0.1" + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" }, "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } } } }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "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 }, - "is-primitive": { + "merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", "dev": true }, - "is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "microevent.ts": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", + "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==", "dev": true }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, "requires": { - "has-symbols": "^1.0.0" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, - "is-there": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/is-there/-/is-there-4.4.4.tgz", - "integrity": "sha512-WbEGbR5i/vSLJ/cc72kVCoM0RvKWmtmPpRXriNlhsredolym2aSTHZA02IzvDR5ewmwD0V6e9S3s9aHs6Ygw5A==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" - }, - "is-whitespace-character": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz", - "integrity": "sha512-SNPgMLz9JzPccD3nPctcj8sZlX9DAMJSKH8bP7Z6bohCwuNgX8xbWr1eTAYXX9Vpi/aSn8Y1akL9WgM3t43YNQ==" - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, - "is-word-character": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.3.tgz", - "integrity": "sha512-0wfcrFgOOOBdgRNT9H33xe6Zi6yhX/uoc4U8NBZGeQQB0ctU1dnlNTyL9JM2646bHDTpsDm1Brb3VPoCIMrd/A==" + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } + } }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "dev": true }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, "requires": { - "punycode": "2.x.x" + "mime-db": "1.44.0" + }, + "dependencies": { + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + } } }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { + "mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-create-react-context": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", + "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", + "dev": true, "requires": { - "isarray": "1.0.0" + "@babel/runtime": "^7.12.1", + "tiny-warning": "^1.0.3" } }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "mini-css-extract-plugin": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.3.tgz", + "integrity": "sha512-7lvliDSMiuZc81kI+5/qxvn47SCM7BehXex3f2c6l/pR3Goj58IQxZh9nuPQ3AkGQgoETyXuIqLDaO5Oa0TyBw==", + "dev": true, "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } } }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "miniget": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/miniget/-/miniget-4.1.0.tgz", + "integrity": "sha512-kzhrNv5L7LlomwGmPGQsLQ2PnT1LeJJWfB0wNFGyv426gEM1gsfziBQmfkr6XOBA8EusZg9nowlNT5CbuKTjZg==", + "dev": true }, - "istanbul-api": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.7.tgz", - "integrity": "sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==", - "dev": true, - "requires": { - "async": "^2.1.4", - "fileset": "^2.0.2", - "istanbul-lib-coverage": "^1.2.1", - "istanbul-lib-hook": "^1.2.2", - "istanbul-lib-instrument": "^1.10.2", - "istanbul-lib-report": "^1.1.5", - "istanbul-lib-source-maps": "^1.2.6", - "istanbul-reports": "^1.5.1", - "js-yaml": "^3.7.0", - "mkdirp": "^0.5.1", - "once": "^1.4.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 + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" } }, - "istanbul-lib-coverage": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", - "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, - "istanbul-lib-hook": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz", - "integrity": "sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==", + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", "dev": true, "requires": { - "append-transform": "^0.4.0" + "yallist": "^4.0.0" } }, - "istanbul-lib-instrument": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", - "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "dev": true, "requires": { - "babel-generator": "^6.18.0", - "babel-template": "^6.16.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", - "babylon": "^6.18.0", - "istanbul-lib-coverage": "^1.2.1", - "semver": "^5.3.0" + "minipass": "^3.0.0" } }, - "istanbul-lib-report": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz", - "integrity": "sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==", + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dev": true, "requires": { - "istanbul-lib-coverage": "^1.2.1", - "mkdirp": "^0.5.1", - "path-parse": "^1.0.5", - "supports-color": "^3.1.2" - }, - "dependencies": { - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } + "minipass": "^3.0.0" } }, - "istanbul-lib-source-maps": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz", - "integrity": "sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==", + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dev": true, "requires": { - "debug": "^3.1.0", - "istanbul-lib-coverage": "^1.2.1", - "mkdirp": "^0.5.1", - "rimraf": "^2.6.1", - "source-map": "^0.5.3" + "minipass": "^3.0.0" } }, - "istanbul-reports": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.1.tgz", - "integrity": "sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==", + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "requires": { - "handlebars": "^4.0.3" + "minipass": "^3.0.0", + "yallist": "^4.0.0" } }, - "jest": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-22.4.3.tgz", - "integrity": "sha512-FFCdU/pXOEASfHxFDOWUysI/+FFoqiXJADEIXgDKuZyqSmBD3tZ4BEGH7+M79v7czj7bbkhwtd2LaEDcJiM/GQ==", + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "mobx": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.0.4.tgz", + "integrity": "sha512-wT2QJT9tW19VSHo9x7RPKU3z/I2Ps6wUS8Kb1OO+kzmg7UY3n4AkcaYG6jq95Lp1R9ohjC/NGYuT2PtuvBjhFg==", + "dev": true + }, + "mobx-react": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-7.0.5.tgz", + "integrity": "sha512-WmHl3Ni30ujVcOOnllmGmyccsbfFCKtAwpkIwncwxhgLcvXcE0Wa9lGJIhoqQdTJzGr0AJqGzMVAdwNc3Fj2DQ==", + "dev": true, + "requires": { + "mobx-react-lite": "^3.1.6" + } + }, + "mobx-react-lite": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.1.6.tgz", + "integrity": "sha512-MM3x9BLt5nC7iE/ILA5n2+hfrplEKYbFjqROEuGkzBdZP/KD+Z44+2gseczRrTG0xFuiPWfEzgT68+6/zqOiEw==", + "dev": true, + "requires": {} + }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", "dev": true, "requires": { - "import-local": "^1.0.0", - "jest-cli": "^22.4.3" + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^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 - }, - "jest-cli": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-22.4.4.tgz", - "integrity": "sha512-I9dsgkeyjVEEZj9wrGrqlH+8OlNob9Iptyl+6L5+ToOLJmHm4JwOPatin1b2Bzp5R5YRQJ+oiedx7o1H7wJzhA==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "import-local": "^1.0.0", - "is-ci": "^1.0.10", - "istanbul-api": "^1.1.14", - "istanbul-lib-coverage": "^1.1.1", - "istanbul-lib-instrument": "^1.8.0", - "istanbul-lib-source-maps": "^1.2.1", - "jest-changed-files": "^22.2.0", - "jest-config": "^22.4.4", - "jest-environment-jsdom": "^22.4.1", - "jest-get-type": "^22.1.0", - "jest-haste-map": "^22.4.2", - "jest-message-util": "^22.4.0", - "jest-regex-util": "^22.1.0", - "jest-resolve-dependencies": "^22.1.0", - "jest-runner": "^22.4.4", - "jest-runtime": "^22.4.4", - "jest-snapshot": "^22.4.0", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "jest-worker": "^22.2.2", - "micromatch": "^2.3.11", - "node-notifier": "^5.2.1", - "realpath-native": "^1.0.0", - "rimraf": "^2.5.4", - "slash": "^1.0.0", - "string-length": "^2.0.0", - "strip-ansi": "^4.0.0", - "which": "^1.2.12", - "yargs": "^10.0.3" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { - "p-try": "^1.0.0" + "ms": "2.0.0" } }, - "p-locate": { + "depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true }, - "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" - } - }, - "which-module": { + "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true - }, - "yargs": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", - "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^8.1.0" - } - }, - "yargs-parser": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } } } }, - "jest-changed-files": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-22.4.3.tgz", - "integrity": "sha512-83Dh0w1aSkUNFhy5d2dvqWxi/y6weDwVVLU6vmK0cV9VpRxPzhTeGimbsbRDSnEoszhF937M4sDLLeS7Cu/Tmw==", - "dev": true, - "requires": { - "throat": "^4.0.0" - } + "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 }, - "jest-config": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-22.4.4.tgz", - "integrity": "sha512-9CKfo1GC4zrXSoMLcNeDvQBfgtqGTB1uP8iDIZ97oB26RCUb886KkKWhVcpyxVDOUxbhN+uzcBCeFe7w+Iem4A==", + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", "dev": true, "requires": { - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^22.4.1", - "jest-environment-node": "^22.4.1", - "jest-get-type": "^22.1.0", - "jest-jasmine2": "^22.4.4", - "jest-regex-util": "^22.1.0", - "jest-resolve": "^22.4.2", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "pretty-format": "^22.4.0" + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" } }, - "jest-diff": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-22.4.3.tgz", - "integrity": "sha512-/QqGvCDP5oZOF6PebDuLwrB2BMD8ffJv6TAGAdEVuDx1+uEgrHpSFrfrOiMRx2eJ1hgNjlQrOQEHetVwij90KA==", + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "multistream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.0.1.tgz", + "integrity": "sha512-LNPIR/LD0JUw2beGlSv4sgTSnGbZp16d/PG2rnIrYjkeCaepNmBTobuiaNQATCPiYgn+BBuQTm70UlvwRfLZ3Q==", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff": "^3.2.0", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" + "readable-stream": "^3.6.0" } }, - "jest-docblock": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-22.4.3.tgz", - "integrity": "sha512-uPKBEAw7YrEMcXueMKZXn/rbMxBiSv48fSqy3uEnmgOlQhSX+lthBqHb1fKWNVmFqAp9E/RsSdBfiV31LbzaOg==", + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true + }, + "nano-css": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.3.0.tgz", + "integrity": "sha512-uM/9NGK9/E9/sTpbIZ/bQ9xOLOIHZwrrb/CRlbDHBU/GFS7Gshl24v/WJhwsVViWkpOXUmiZ66XO7fSB4Wd92Q==", "dev": true, "requires": { - "detect-newline": "^2.1.0" + "css-tree": "^1.0.0-alpha.28", + "csstype": "^2.5.5", + "fastest-stable-stringify": "^1.0.1", + "inline-style-prefixer": "^4.0.0", + "rtl-css-js": "^1.9.0", + "sourcemap-codec": "^1.4.1", + "stacktrace-js": "^2.0.0", + "stylis": "3.5.0" + }, + "dependencies": { + "csstype": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz", + "integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==", + "dev": true + } } }, - "jest-environment-jsdom": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz", - "integrity": "sha512-FviwfR+VyT3Datf13+ULjIMO5CSeajlayhhYQwpzgunswoaLIPutdbrnfUHEMyJCwvqQFaVtTmn9+Y8WCt6n1w==", + "nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, "requires": { - "jest-mock": "^22.4.3", - "jest-util": "^22.4.3", - "jsdom": "^11.5.1" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" } }, - "jest-environment-node": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-22.4.3.tgz", - "integrity": "sha512-reZl8XF6t/lMEuPWwo9OLfttyC26A5AMgDyEQ6DBgZuyfyeNUzYT8BFo6uxCCP/Av/b7eb9fTi3sIHFPBzmlRA==", + "native-url": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/native-url/-/native-url-0.2.6.tgz", + "integrity": "sha512-k4bDC87WtgrdD362gZz6zoiXQrl40kYlBmpfmSjwRO1VU0V5ccwJTlxuE72F6m3V0vc1xOf6n3UCP9QyerRqmA==", "dev": true, "requires": { - "jest-mock": "^22.4.3", - "jest-util": "^22.4.3" + "querystring": "^0.2.0" } }, - "jest-get-type": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", - "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "jest-haste-map": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-22.4.3.tgz", - "integrity": "sha512-4Q9fjzuPVwnaqGKDpIsCSoTSnG3cteyk2oNVjBX12HHOaF1oxql+uUiqZb5Ndu7g/vTZfdNwwy4WwYogLh29DQ==", - "dev": true, - "requires": { - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.1.11", - "jest-docblock": "^22.4.3", - "jest-serializer": "^22.4.3", - "jest-worker": "^22.4.3", - "micromatch": "^2.3.11", - "sane": "^2.0.0" - } - }, - "jest-jasmine2": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-22.4.4.tgz", - "integrity": "sha512-nK3vdUl50MuH7vj/8at7EQVjPGWCi3d5+6aCi7Gxy/XMWdOdbH1qtO/LjKbqD8+8dUAEH+BVVh7HkjpCWC1CSw==", + "nedb": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz", + "integrity": "sha1-DjUCzYLABNU1WkPJ5VV3vXvZHYg=", "dev": true, "requires": { - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^22.4.0", - "graceful-fs": "^4.1.11", - "is-generator-fn": "^1.0.0", - "jest-diff": "^22.4.0", - "jest-matcher-utils": "^22.4.0", - "jest-message-util": "^22.4.0", - "jest-snapshot": "^22.4.0", - "jest-util": "^22.4.1", - "source-map-support": "^0.5.0" + "async": "0.2.10", + "binary-search-tree": "0.2.5", + "localforage": "^1.3.0", + "mkdirp": "~0.5.1", + "underscore": "~1.4.4" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", "dev": true }, - "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "minimist": "^1.2.5" } } } }, - "jest-leak-detector": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-22.4.3.tgz", - "integrity": "sha512-NZpR/Ls7+ndO57LuXROdgCGz2RmUdC541tTImL9bdUtU3WadgFGm0yV+Ok4Fuia/1rLAn5KaJ+i76L6e3zGJYQ==", + "nedb-promises": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/nedb-promises/-/nedb-promises-4.1.0.tgz", + "integrity": "sha512-nTdx7jX/Vu24L05Cy0ee7CL3L4SEHCb1jlLlegPl0VlE8jsUXgnSyNOjq3FEc3cdUSDK05X7hSzb3+a07PigmQ==", "dev": true, "requires": { - "pretty-format": "^22.4.3" + "nedb": "^1.8.0" } }, - "jest-matcher-utils": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz", - "integrity": "sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-get-type": "^22.4.3", - "pretty-format": "^22.4.3" - } + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true }, - "jest-message-util": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-22.4.3.tgz", - "integrity": "sha512-iAMeKxhB3Se5xkSjU0NndLLCHtP4n+GtCqV0bISKA5dmOXQfEbdEmYiu2qpnWBDCQdEafNDDU6Q+l6oBMd/+BA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0-beta.35", - "chalk": "^2.0.1", - "micromatch": "^2.3.11", - "slash": "^1.0.0", - "stack-utils": "^1.0.1" - } + "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 }, - "jest-mock": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-22.4.3.tgz", - "integrity": "sha512-+4R6mH5M1G4NK16CKg9N1DtCaFmuxhcIqF4lQK/Q1CIotqMs/XBemfpDPeVZBFow6iyUNu6EBT9ugdNOTT5o5Q==", + "newsemitter": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/newsemitter/-/newsemitter-1.0.2.tgz", + "integrity": "sha512-N9jDBdccPetXfeM5/VkUYZewdVLQ3FzcjCuMqkJCze3tDEViP9cba0aK0oHl19Jj7GkfNxsEtntRhIzgAJ8HiA==", "dev": true }, - "jest-regex-util": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-22.4.3.tgz", - "integrity": "sha512-LFg1gWr3QinIjb8j833bq7jtQopiwdAs67OGfkPrvy7uNUbVMfTXXcOKXJaeY5GgjobELkKvKENqq1xrUectWg==", + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "jest-resolve": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-22.4.3.tgz", - "integrity": "sha512-u3BkD/MQBmwrOJDzDIaxpyqTxYH+XqAXzVJP51gt29H8jpj3QgKof5GGO2uPGKGeA1yTMlpbMs1gIQ6U4vcRhw==", + "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": { - "browser-resolve": "^1.11.2", - "chalk": "^2.0.1" + "lower-case": "^2.0.2", + "tslib": "^2.0.3" } }, - "jest-resolve-dependencies": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-22.4.3.tgz", - "integrity": "sha512-06czCMVToSN8F2U4EvgSB1Bv/56gc7MpCftZ9z9fBgUQM7dzHGCMBsyfVA6dZTx8v0FDcnALf7hupeQxaBCvpA==", - "dev": true, - "requires": { - "jest-regex-util": "^22.4.3" - } + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true }, - "jest-runner": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-22.4.4.tgz", - "integrity": "sha512-5S/OpB51igQW9xnkM5Tgd/7ZjiAuIoiJAVtvVTBcEBiXBIFzWM3BAMPBM19FX68gRV0KWyFuGKj0EY3M3aceeQ==", + "node-gyp": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", + "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==", "dev": true, "requires": { - "exit": "^0.1.2", - "jest-config": "^22.4.4", - "jest-docblock": "^22.4.0", - "jest-haste-map": "^22.4.2", - "jest-jasmine2": "^22.4.4", - "jest-leak-detector": "^22.4.0", - "jest-message-util": "^22.4.0", - "jest-runtime": "^22.4.4", - "jest-util": "^22.4.1", - "jest-worker": "^22.2.2", - "throat": "^4.0.0" + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, - "jest-runtime": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-22.4.4.tgz", - "integrity": "sha512-WRTj9m///npte1YjuphCYX7GRY/c2YvJImU9t7qOwFcqHr4YMzmX6evP/3Sehz5DKW2Vi8ONYPCFWe36JVXxfw==", + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", "dev": true, "requires": { - "babel-core": "^6.0.0", - "babel-jest": "^22.4.4", - "babel-plugin-istanbul": "^4.1.5", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "exit": "^0.1.2", - "graceful-fs": "^4.1.11", - "jest-config": "^22.4.4", - "jest-haste-map": "^22.4.2", - "jest-regex-util": "^22.1.0", - "jest-resolve": "^22.4.2", - "jest-util": "^22.4.1", - "jest-validate": "^22.4.4", - "json-stable-stringify": "^1.0.1", - "micromatch": "^2.3.11", - "realpath-native": "^1.0.0", - "slash": "^1.0.0", - "strip-bom": "3.0.0", - "write-file-atomic": "^2.1.0", - "yargs": "^10.0.3" + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "babel-jest": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-22.4.4.tgz", - "integrity": "sha512-A9NB6/lZhYyypR9ATryOSDcqBaqNdzq4U+CN+/wcMsLcmKkPxQEoTKLajGfd3IkxNyVBT8NewUK2nWyGbSzHEQ==", + "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": { - "babel-plugin-istanbul": "^4.1.5", - "babel-preset-jest": "^22.4.4" + "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" } }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "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 }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "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": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "safe-buffer": "~5.1.0" } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + } + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz", + "integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==", + "dev": true, + "optional": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, + "optional": true, "requires": { - "locate-path": "^2.0.0" + "lru-cache": "^6.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=", + } + } + }, + "node-releases": { + "version": "1.1.67", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz", + "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==", + "dev": true + }, + "node-sass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-5.0.0.tgz", + "integrity": "sha512-opNgmlu83ZCF792U281Ry7tak9IbVC+AKnXGovcQ8LG8wFaJv6cLnRlc6DIHlmNxWEexB5bZxi9SZ9JyUuOYjw==", + "dev": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^7.0.3", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "lodash": "^4.17.15", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^7.1.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "2.2.5", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, - "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=", + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "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 }, - "yargs": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", - "integrity": "sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==", + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^8.1.0" + "minimist": "^1.2.5" } }, - "yargs-parser": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", - "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "camelcase": "^4.1.0" + "ansi-regex": "^2.0.0" } - } - } - }, - "jest-serializer": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-22.4.3.tgz", - "integrity": "sha512-uPaUAppx4VUfJ0QDerpNdF43F68eqKWCzzhUlKNDsUPhjOon7ZehR4C809GCqh765FoMRtTVUVnGvIoskkYHiw==", - "dev": true - }, - "jest-snapshot": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-22.4.3.tgz", - "integrity": "sha512-JXA0gVs5YL0HtLDCGa9YxcmmV2LZbwJ+0MfyXBBc5qpgkEYITQFJP7XNhcHFbUvRiniRpRbGVfJrOoYhhGE0RQ==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^22.4.3", - "jest-matcher-utils": "^22.4.3", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^22.4.3" - } - }, - "jest-util": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-22.4.3.tgz", - "integrity": "sha512-rfDfG8wyC5pDPNdcnAlZgwKnzHvZDu8Td2NJI/jAGKEGxJPYiE4F0ss/gSAkG4778Y23Hvbz+0GMrDJTeo7RjQ==", - "dev": true, - "requires": { - "callsites": "^2.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.11", - "is-ci": "^1.0.10", - "jest-message-util": "^22.4.3", - "mkdirp": "^0.5.1", - "source-map": "^0.6.0" - }, - "dependencies": { - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true } } }, - "jest-validate": { - "version": "22.4.4", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-22.4.4.tgz", - "integrity": "sha512-dmlf4CIZRGvkaVg3fa0uetepcua44DHtktHm6rcoNVtYlpwe6fEJRkMFsaUVcFHLzbuBJ2cPw9Gl9TKfnzMVwg==", + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-config": "^22.4.4", - "jest-get-type": "^22.1.0", - "leven": "^2.1.0", - "pretty-format": "^22.4.0" + "abbrev": "1" } }, - "jest-worker": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-22.4.3.tgz", - "integrity": "sha512-B1ucW4fI8qVAuZmicFxI1R3kr2fNeYJyvIQ1rKcuLYnenFV5K5aMbxFj6J0i00Ju83S8jP2d7Dz14+AvbIHRYQ==", + "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": { - "merge-stream": "^1.0.1" - } - }, - "joi": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz", - "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==", - "requires": { - "hoek": "5.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "js-base64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", - "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==" - }, - "js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==" + "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 }, - "js-stringify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + "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 }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "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": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - } + "path-key": "^3.0.0" } }, - "js2xmlparser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.0.tgz", - "integrity": "sha512-WuNgdZOXVmBk5kUPMcTcVUpbGRzLfNkv7+7APq7WiDihpXVKrgxo6wwRpRl9OQeEBgKCVk9mR7RbzrnNWC8oBw==", + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "requires": { - "xmlcreate": "^2.0.0" - } - }, - "jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=" - }, - "jsdoc": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.3.tgz", - "integrity": "sha512-Yf1ZKA3r9nvtMWHO1kEuMZTlHOF8uoQ0vyo5eH7SQy5YeIiHM+B0DgKnn+X6y6KDYZcF7G2SPkKF+JORCXWE/A==", - "dev": true, - "requires": { - "@babel/parser": "^7.4.4", - "bluebird": "^3.5.4", - "catharsis": "^0.8.11", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.0", - "klaw": "^3.0.0", - "markdown-it": "^8.4.2", - "markdown-it-anchor": "^5.0.2", - "marked": "^0.7.0", - "mkdirp": "^0.5.1", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.0.1", - "taffydb": "2.6.2", - "underscore": "~1.9.1" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", - "dev": true - }, - "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", - "dev": true - } - } - }, - "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.1", - "escodegen": "^1.9.1", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", - "dev": true - }, - "acorn-globals": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", - "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", - "dev": true, - "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" - }, - "dependencies": { - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - } - } - } + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-parse-better-errors": { + "nth-check": { "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-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "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==" - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dev": true, "requires": { - "jsonify": "~0.0.0" + "boolbase": "~1.0.0" } }, - "json-stable-stringify-without-jsonify": { + "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", "dev": true }, - "json5": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.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 }, - "jstransformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, "requires": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { - "asap": "~2.0.3" + "is-buffer": "^1.1.5" } } } }, - "jsx-ast-utils": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", - "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==", + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true + }, + "object-is": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz", + "integrity": "sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==", "dev": true, "requires": { - "array-includes": "^3.0.3", - "object.assign": "^4.1.0" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } + "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 }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" + "isobject": "^3.0.0" } }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "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": { - "is-buffer": "^1.1.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" } }, - "klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "object.entries": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", + "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", "dev": true, "requires": { - "graceful-fs": "^4.1.9" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" } }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "object.fromentries": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.3.tgz", + "integrity": "sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw==", "dev": true, "requires": { - "package-json": "^4.0.0" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" } }, - "lazy": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", - "integrity": "sha1-2qBoIGKCVCwIgojpdcKXwa53tpA=" - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "object.getownpropertydescriptors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", + "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", + "dev": true, "requires": { - "invert-kv": "^1.0.0" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" } }, - "left-pad": { + "object.pick": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, - "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "isobject": "^3.0.1" } }, - "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "object.values": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", + "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", + "dev": true, "requires": { - "immediate": "~3.0.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" } }, - "linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "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": { - "uc.micro": "^1.0.1" + "ee-first": "1.1.1" } }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "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=", "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } + "wrappy": "1" } }, - "loader-fs-cache": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz", - "integrity": "sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw==", + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "requires": { - "find-cache-dir": "^0.1.1", - "mkdirp": "0.5.1" - }, - "dependencies": { - "find-cache-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", - "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true, - "requires": { - "find-up": "^1.0.0" - } - } + "mimic-fn": "^2.1.0" } }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "open": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz", + "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==", + "dev": true, "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - } + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" } }, - "localforage": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.3.tgz", - "integrity": "sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==", + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, "requires": { - "lie": "3.1.1" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "url-parse": "^1.4.3" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, - "lodash-es": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", - "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "overlayscrollbars": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.0.tgz", + "integrity": "sha512-p8oHrMeRAKxXDMPI/EBNITj/zTVHKNnAnM59Im+xnoZUlV07FyTg46wom2286jJlXGGfcPFG/ba5NUiCwWNd4w==", "dev": true }, - "lodash.unescape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", - "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=" + "overlayscrollbars-react": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/overlayscrollbars-react/-/overlayscrollbars-react-0.2.2.tgz", + "integrity": "sha512-sRJDaKIxD+No6ygMRaCxejuIH2CksSCUTfaDOzDhPt22lI3IPZq/+Ifw2RT4j+U7hgXLn9P5QqA00f2bsZVwPA==", + "dev": true, + "requires": {} }, - "loglevel": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", - "integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==", + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", "dev": true }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + "p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "p-timeout": "^3.1.0" } }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "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": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "yocto-queue": "^0.1.0" } }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true + "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" + }, + "dependencies": { + "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" + } + } + } }, - "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==", + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "aggregate-error": "^3.0.0" } }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "@types/retry": "^0.12.0", + "retry": "^0.12.0" } }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "dev": true, "requires": { - "tmpl": "1.0.x" + "p-finally": "^1.0.0" } }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "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 }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "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": { - "p-defer": "^1.0.0" + "dot-case": "^3.0.4", + "tslib": "^2.0.3" } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-obj": { + "parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "requires": { - "object-visit": "^1.0.0" + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } } }, - "markdown-escapes": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.3.tgz", - "integrity": "sha512-XUi5HJhhV5R74k8/0H2oCbCiYf/u4cO/rX8tnGkRvrqhsr5BRNU6Mg0yt/8UIx1iIS8220BNJsDb7XnILhLepw==" + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } }, - "markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } }, - "markdown-it-anchor": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.5.tgz", - "integrity": "sha512-xLIjLQmtym3QpoY9llBgApknl7pxAcN3WDRc2d3rwpl+/YvDZHPmKscGs+L6E05xf2KrCXPBvosWt7MZukwSpQ==", + "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 }, - "marked": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, - "math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "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" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "passport": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", + "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", "dev": true, "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "passport-strategy": "1.x.x", + "pause": "0.0.1" } }, - "mdast-add-list-metadata": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz", - "integrity": "sha512-fB/VP4MJ0LaRsog7hGPxgOrSL3gE/2uEdZyDuSEnKCv/8IkYHiDkIQSbChiJoHyxZZXZ9bzckyRk+vNxFzh8rA==", + "passport-jwt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz", + "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==", + "dev": true, "requires": { - "unist-util-visit-parents": "1.1.2" + "jsonwebtoken": "^8.2.0", + "passport-strategy": "^1.0.0" } }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", "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=" + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "requires": { - "mimic-fn": "^1.0.0" - } + "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 }, - "memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", + "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=" + }, + "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 }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - } + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true }, - "merge": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", - "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", + "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 }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "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 }, - "merge-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-1.0.1.tgz", - "integrity": "sha512-iuPV41VWKWBIOpBsjoxjDZw8/GbSfZ2mk7N1453bwMrfzdrIk7EzBd+8UVR6rkw67th7xnk9Dytl3J+lHPdxvg==", - "requires": { - "is-plain-obj": "^1.1" - } + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=", + "dev": true }, - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", "dev": true, "requires": { - "readable-stream": "^2.0.1" + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, - "merge2": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "piece-length": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/piece-length/-/piece-length-2.0.1.tgz", + "integrity": "sha512-dBILiDmm43y0JPISWEmVGKBETQjwJe6mSU9GND+P9KW0SJGUwoU/odyH1nbalOP9i8WSYuqf1lQnaj92Bhw+Ug==", "dev": true }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true }, - "microevent.ts": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", - "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==", + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", "dev": true }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" } }, - "miller-rabin": { + "pirates": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", "dev": true, "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "node-modules-regexp": "^1.0.0" } }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, "requires": { - "mime-db": "1.40.0" + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "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==" - }, - "minami": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/minami/-/minami-1.2.3.tgz", - "integrity": "sha1-mbbc37LwpU2hycj3qjoyd4eq+fg=", - "dev": true - }, - "mini-create-react-context": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz", - "integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==", + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, "requires": { - "@babel/runtime": "^7.4.0", - "gud": "^1.0.0", - "tiny-warning": "^1.0.2" + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } } }, - "miniget": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/miniget/-/miniget-1.5.1.tgz", - "integrity": "sha512-KJ3AyIVZ76QuWAq43BWjkK+jLdhxhy3s4tsdg9Je91+cIFkeOSW2VEj2lSeKw50CPu1eCCkSbiQEBKL36mpA5w==" - }, - "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==" + "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" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + } + } }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "postcss": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.1.tgz", + "integrity": "sha512-RhsqOOAQzTgh1UB/IZdca7F9WDb7SUCR2Vnv1x7DbvuuggQIpoDwjK+q0rzoPffhYvWNKX5JSwS4so4K3UC6vA==", + "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "colorette": "^1.2.1", + "nanoid": "^3.1.20", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "mitt": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.1.2.tgz", - "integrity": "sha1-OA5hSA1qYVtmDwertg1R4KTkvtY=" - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "has-flag": "^3.0.0" } } } }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, "requires": { - "minimist": "0.0.8" + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, - "morgan": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", - "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, "requires": { - "basic-auth": "~2.0.0", - "debug": "2.6.9", - "depd": "~1.1.2", - "on-finished": "~2.3.0", - "on-headers": "~1.0.1" + "postcss": "^7.0.0" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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": { - "ms": "2.0.0" + "color-convert": "^1.9.0" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "multer": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", - "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", - "requires": { - "append-field": "^1.0.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.1", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - } - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", "dev": true, "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", - "requires": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" + "postcss": "^7.0.0" }, "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" } }, - "rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "requires": { - "glob": "^6.0.1" + "has-flag": "^3.0.0" } } } }, - "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "postcss": "^7.0.0" }, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + "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" + } }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + "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" + }, + "dependencies": { + "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" + } + } + } }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" - }, - "nedb": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz", - "integrity": "sha1-DjUCzYLABNU1WkPJ5VV3vXvZHYg=", - "requires": { - "async": "0.2.10", - "binary-search-tree": "0.2.5", - "localforage": "^1.3.0", - "mkdirp": "~0.5.1", - "underscore": "~1.4.4" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" - }, - "neo-async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", - "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==" - }, - "newsemitter": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/newsemitter/-/newsemitter-0.3.1.tgz", - "integrity": "sha512-ZlY1PnxxKqKEBZNJsbE3pUsZ4vVd8Y9HsbXj8rSIEYkUnh+vp/E/aXdo4VQjyRXBMfsp2VOGz/CIYW9TsIvedQ==" - }, - "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": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "requires": { - "lower-case": "^1.1.1" - } - }, - "node-addon-api": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.1.tgz", - "integrity": "sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ==" - }, - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "node-forge": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", - "dev": true - }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" + "postcss": "^7.0.0" }, "dependencies": { - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "node-gyp-build": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz", - "integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==" - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-libs-browser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", - "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "postcss-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.1.0.tgz", + "integrity": "sha512-vbCkP70F3Q9PIk6d47aBwjqAMI4LfkXCoyxj+7NPNuVIwfTGdzv2KVQes59/RuxMniIgsYQCFSY42P3+ykJfaw==", "dev": true, "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "0.0.4" + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" }, "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "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" + } + }, + "import-fresh": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "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" + } + }, + "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 + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, - "node-notifier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", - "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", "dev": true, "requires": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" - } - }, - "node-releases": { - "version": "1.1.41", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.41.tgz", - "integrity": "sha512-+IctMa7wIs8Cfsa8iYzeaLTFwv5Y4r5jZud+4AnfymzeEXKBCavFX0KBgzVaPVqf0ywa6PrO8/b+bPqdwjGBSg==", - "requires": { - "semver": "^6.3.0" + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" }, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "node-sass": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.0.tgz", - "integrity": "sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==", + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash": "^4.17.15", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.13.2", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "^2.2.4", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" }, "dependencies": { "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + "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" + } }, "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" } }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "nodemon": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz", - "integrity": "sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ==", + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", "dev": true, "requires": { - "chokidar": "^2.1.8", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 - } - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "requires": { - "abbrev": "1" - } - }, - "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==", - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" - }, - "normalize.css": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-7.0.0.tgz", - "integrity": "sha1-q/sd2CRwZ04DIrU86xqvQSk45L8=" - }, - "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=", - "requires": { - "path-key": "^2.0.0" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "requires": { - "boolbase": "~1.0.0" + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "nwsapi": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.3.tgz", - "integrity": "sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "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": { - "is-descriptor": "^0.1.0" + "color-convert": "^1.9.0" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" } } } }, - "object-hash": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", - "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", - "dev": true - }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "object-is": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", - "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, "requires": { - "isobject": "^3.0.0" + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" }, "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.entries": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", - "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "object.fromentries": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.1.tgz", - "integrity": "sha512-PUQv8Hbg3j2QX0IQYv3iAGCbGcu4yY4KQ92/dhA4sFSixBmSmp13UpDLs6jGK8rBtbmhNNIK99LD2k293jpiGA==", + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.15.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" }, "dependencies": { - "es-abstract": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", - "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.0", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", - "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.1.0", - "string.prototype.trimright": "^2.1.0" + "has-flag": "^3.0.0" } } } }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "object.values": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", - "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "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, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "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==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } + "requires": {} }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "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": { - "mimic-fn": "^1.0.0" + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" } }, - "open": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", - "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "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": { - "is-wsl": "^1.1.0" + "postcss-selector-parser": "^6.0.4" } }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "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": { - "is-wsl": "^1.1.0" + "icss-utils": "^5.0.0" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "postcss": "^7.0.0" }, "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 - } - } - }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "requires": { - "lcid": "^1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=" - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - }, - "p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", - "dev": true, - "requires": { - "retry": "^0.12.0" - } - }, - "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==" - }, - "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", - "dev": true, - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - } - }, - "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", - "dev": true - }, - "parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "requires": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "requires": { - "no-case": "^2.2.0" + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "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==", + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", "dev": true, "requires": { - "callsites": "^3.0.0" + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "parse-asn1": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", - "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", "dev": true, "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-entities": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", - "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", - "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", "dev": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "pascal-case": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", - "integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=", - "requires": { - "camel-case": "^3.0.0", - "upper-case-first": "^1.1.0" - } - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "passport": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", - "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", - "requires": { - "passport-strategy": "1.x.x", - "pause": "0.0.1" - } - }, - "passport-jwt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz", - "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==", - "requires": { - "jsonwebtoken": "^8.2.0", - "passport-strategy": "^1.0.0" + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "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=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "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=" - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", "dev": true, "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { + "postcss-normalize-url": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, "requires": { - "find-up": "^3.0.0" + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", "dev": true, "requires": { - "find-up": "^2.1.0" + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "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": { - "locate-path": "^2.0.0" + "color-convert": "^1.9.0" } }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "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": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "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" + } + } } }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "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": { - "p-try": "^1.0.0" + "color-name": "1.1.3" } }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", "dev": true, "requires": { - "p-limit": "^1.1.0" + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" } }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true - } - } - }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "portfinder": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", - "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.1" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - }, - "postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - }, - "dependencies": { + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, - "postcss-load-config": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", - "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", - "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0", - "postcss-load-options": "^1.2.0", - "postcss-load-plugins": "^2.3.0" - } - }, - "postcss-load-options": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", - "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", - "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0" - } - }, - "postcss-load-plugins": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", - "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", - "requires": { - "cosmiconfig": "^2.1.1", - "object-assign": "^4.1.0" - } - }, - "postcss-loader": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.3.tgz", - "integrity": "sha512-RuBcNE8rjCkIB0IsbmkGFRmQJTeQJfCI88E0VTarPNTvaNSv9OFv1DvTwgtAN/qlzyiELsmmmtX/tEzKp/cdug==", + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, "requires": { - "loader-utils": "^1.1.0", - "postcss": "^6.0.0", - "postcss-load-config": "^1.2.0", - "schema-utils": "^0.4.0" + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "dependencies": { - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "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": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "color-convert": "^1.9.0" } }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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" + } }, - "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==" + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "has-flag": "^3.0.0" } } } }, - "postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, "requires": { - "postcss": "^7.0.5" + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" }, "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "postcss": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.23.tgz", - "integrity": "sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==", + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", @@ -13480,33 +39658,95 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, - "postcss-modules-local-by-default": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", - "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, "requires": { - "icss-utils": "^4.1.1", - "postcss": "^7.0.16", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.0" + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" }, "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "postcss": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.23.tgz", - "integrity": "sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==", + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", @@ -13514,105 +39754,218 @@ } }, "postcss-value-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", - "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, - "postcss-modules-scope": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", - "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==", + "postcss-selector-parser": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", + "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", + "dev": true, "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" + "is-svg": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" }, "dependencies": { - "postcss": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.23.tgz", - "integrity": "sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==", + "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": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "color-convert": "^1.9.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "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" + }, + "dependencies": { + "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-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "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": { - "has-flag": "^3.0.0" + "color-name": "1.1.3" } - } - } - }, - "postcss-modules-values": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", - "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", - "requires": { - "icss-utils": "^4.0.0", - "postcss": "^7.0.6" - }, - "dependencies": { + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "postcss": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.23.tgz", - "integrity": "sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==", + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", "supports-color": "^6.1.0" } }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, - "postcss-prefix-selector": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/postcss-prefix-selector/-/postcss-prefix-selector-1.7.2.tgz", - "integrity": "sha512-ddmzjWNmGs7E/nyolJ021/Gk6oBLRQLyyXKGV4Mu+Y0gquo+XlXSDP0/Y2J8C/cad/GLyftf2H0XtuDFQZxN3w==", + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, "requires": { - "postcss": "^7.0.0" + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" }, "dependencies": { + "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" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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 + }, + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "postcss": { - "version": "7.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", - "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", @@ -13622,135 +39975,65 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "requires": { "has-flag": "^3.0.0" } } } }, - "postcss-selector-parser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", - "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", - "requires": { - "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - }, - "posthtml": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.9.2.tgz", - "integrity": "sha1-9MBtufZ7Yf0XxOJW5+PZUVv3Jv0=", - "requires": { - "posthtml-parser": "^0.2.0", - "posthtml-render": "^1.0.5" - } - }, - "posthtml-parser": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.2.1.tgz", - "integrity": "sha1-NdUw3jhnQMK6JP8usvrznM3ycd0=", - "requires": { - "htmlparser2": "^3.8.3", - "isobject": "^2.1.0" - } - }, - "posthtml-rename-id": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/posthtml-rename-id/-/posthtml-rename-id-1.0.11.tgz", - "integrity": "sha512-8doF8+w+WJT4AZuLVC0feA8Yy7g00IUmZw3YDKn8CKx0uC8FLbCH7JaGMbDOE1ArjyZsJMt1vmyP+IZ8SnNmXw==", - "requires": { - "escape-string-regexp": "1.0.5" - } - }, - "posthtml-render": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-1.1.5.tgz", - "integrity": "sha512-yvt54j0zCBHQVEFAuR+yHld8CZrCa/E1Z/OcFNCV1IEWTLVxT8O7nYnM4IIw1CD4r8kaRd3lc42+0lgCKgm87w==" - }, - "posthtml-svg-mode": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/posthtml-svg-mode/-/posthtml-svg-mode-1.0.3.tgz", - "integrity": "sha512-hEqw9NHZ9YgJ2/0G7CECOeuLQKZi8HjWLkBaSVtOWjygQ9ZD8P7tqeowYs7WrFdKsWEKG7o+IlsPY8jrr0CJpQ==", - "requires": { - "merge-options": "1.0.1", - "posthtml": "^0.9.2", - "posthtml-parser": "^0.2.1", - "posthtml-render": "^1.0.6" - } - }, - "prefix-style": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/prefix-style/-/prefix-style-2.0.1.tgz", - "integrity": "sha1-ZrupqHDP2jCKXcIOhekSCTLJWgY=" - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", "dev": true }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "prettier": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.2.tgz", - "integrity": "sha512-McHPg0n1pIke+A/4VcaS2en+pTNjy4xF+Uuq86u/5dyDO59/TtFZtQ708QIRkEZ3qwKz3GVkVa6mpxK/CpB8Rg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", "dev": true }, "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "dev": true, "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" + "lodash": "^4.17.20", + "renderkid": "^2.0.4" } }, "pretty-format": { - "version": "22.4.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-22.4.3.tgz", - "integrity": "sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", "dev": true, "requires": { - "ansi-regex": "^3.0.0", - "ansi-styles": "^3.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" } }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + "pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", + "dev": true }, "process": { "version": "0.11.10", @@ -13759,9 +40042,10 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "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": { "version": "2.0.3", @@ -13770,9 +40054,10 @@ "dev": true }, "promise": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.0.3.tgz", - "integrity": "sha512-HeRDUL1RJiLhyA0/grn+PTShlBAcLuh/1BJGtrvjwbvRDCTLLMEz9rOGCV+R3vHY4MixIuoMEd9Yq/XvsTPcjw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "dev": true, "requires": { "asap": "~2.0.6" } @@ -13780,261 +40065,113 @@ "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prompts": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", + "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } }, "prop-types": { "version": "15.7.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.8.1" - } - }, - "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" - }, - "pstree.remy": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", - "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pug": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz", - "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==", - "requires": { - "pug-code-gen": "^2.0.2", - "pug-filters": "^3.1.1", - "pug-lexer": "^4.1.0", - "pug-linker": "^3.0.6", - "pug-load": "^2.0.12", - "pug-parser": "^5.0.1", - "pug-runtime": "^2.0.5", - "pug-strip-comments": "^1.0.4" - } - }, - "pug-attrs": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz", - "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==", - "requires": { - "constantinople": "^3.0.1", - "js-stringify": "^1.0.1", - "pug-runtime": "^2.0.5" - } - }, - "pug-code-gen": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz", - "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==", - "requires": { - "constantinople": "^3.1.2", - "doctypes": "^1.1.0", - "js-stringify": "^1.0.1", - "pug-attrs": "^2.0.4", - "pug-error": "^1.3.3", - "pug-runtime": "^2.0.5", - "void-elements": "^2.0.1", - "with": "^5.0.0" - } - }, - "pug-error": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz", - "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ==" - }, - "pug-filters": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz", - "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==", - "requires": { - "clean-css": "^4.1.11", - "constantinople": "^3.0.1", - "jstransformer": "1.0.0", - "pug-error": "^1.3.3", - "pug-walk": "^1.1.8", - "resolve": "^1.1.6", - "uglify-js": "^2.6.1" }, "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - } - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true } } }, - "pug-lexer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz", - "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==", - "requires": { - "character-parser": "^2.1.1", - "is-expression": "^3.0.0", - "pug-error": "^1.3.3" - } - }, - "pug-linker": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz", - "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==", - "requires": { - "pug-error": "^1.3.3", - "pug-walk": "^1.1.8" - } - }, - "pug-load": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz", - "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==", - "requires": { - "object-assign": "^4.1.0", - "pug-walk": "^1.1.8" - } - }, - "pug-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz", - "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==", + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, "requires": { - "pug-error": "^1.3.3", - "token-stream": "0.0.1" + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" } }, - "pug-runtime": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz", - "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw==" + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true }, - "pug-strip-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz", - "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==", + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, "requires": { - "pug-error": "^1.3.3" + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "dev": true + } } }, - "pug-walk": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz", - "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==" - }, "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" } }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "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==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true }, "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "version": "6.13.7", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.7.tgz", + "integrity": "sha512-CsGs8ZYb39zu0WLkeOhe0NMePqgYdAuCqxOYKDR5LVCytDZYMGx3Bb+xypvQvPHVPijRXB0HZNFllCzHRe4gEA==", + "dev": true, "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" } }, "querystring": { @@ -14050,48 +40187,16 @@ "dev": true }, "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, - "raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "requires": { - "performance-now": "^2.1.0" - } - }, - "ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==" - }, - "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "dev": true, - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } + "queue-microtask": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz", + "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==", + "dev": true }, "randombytes": { "version": "2.1.0", @@ -14115,301 +40220,301 @@ "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==" + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true }, "raw-body": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, "requires": { "bytes": "3.1.0", "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } } }, "react": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz", - "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz", + "integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==", + "dev": true, "requires": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - } - }, - "react-custom-scrollbars": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz", - "integrity": "sha1-gw/ZUCkn6X6KeMIIaBOJmyqLZts=", - "requires": { - "dom-css": "^2.0.0", - "prop-types": "^15.5.10", - "raf": "^3.1.0" + "object-assign": "^4.1.1" } }, "react-dev-utils": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.1.0.tgz", - "integrity": "sha512-X2KYF/lIGyGwP/F/oXgGDF24nxDA2KC4b7AFto+eqzc/t838gpSGiaU8trTqHXOohuLxxc5qi1eDzsl9ucPDpg==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.1.tgz", + "integrity": "sha512-rlgpCupaW6qQqvu0hvv2FDv40QG427fjghV56XyPcP5aKtOAPzNAhQ7bHqk1YdS2vpW1W7aSV3JobedxuPlBAA==", "dev": true, "requires": { - "@babel/code-frame": "7.5.5", + "@babel/code-frame": "7.10.4", "address": "1.1.2", - "browserslist": "4.7.0", + "browserslist": "4.14.2", "chalk": "2.4.2", - "cross-spawn": "6.0.5", + "cross-spawn": "7.0.3", "detect-port-alt": "1.1.6", - "escape-string-regexp": "1.0.5", - "filesize": "3.6.1", - "find-up": "3.0.0", - "fork-ts-checker-webpack-plugin": "1.5.0", + "escape-string-regexp": "2.0.0", + "filesize": "6.1.0", + "find-up": "4.1.0", + "fork-ts-checker-webpack-plugin": "4.1.6", "global-modules": "2.0.0", - "globby": "8.0.2", + "globby": "11.0.1", "gzip-size": "5.1.1", - "immer": "1.10.0", - "inquirer": "6.5.0", + "immer": "7.0.9", "is-root": "2.1.0", - "loader-utils": "1.2.3", - "open": "^6.3.0", - "pkg-up": "2.0.0", - "react-error-overlay": "^6.0.3", + "loader-utils": "2.0.0", + "open": "^7.0.2", + "pkg-up": "3.1.0", + "prompts": "2.4.0", + "react-error-overlay": "^6.0.8", "recursive-readdir": "2.2.2", "shell-quote": "1.7.2", - "sockjs-client": "1.4.0", - "strip-ansi": "5.2.0", + "strip-ansi": "6.0.0", "text-table": "0.2.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "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": { - "@babel/highlight": "^7.0.0" + "color-convert": "^1.9.0" } }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, "browserslist": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz", - "integrity": "sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz", + "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000989", - "electron-to-chromium": "^1.3.247", - "node-releases": "^1.1.29" + "caniuse-lite": "^1.0.30001125", + "electron-to-chromium": "^1.3.564", + "escalade": "^3.0.2", + "node-releases": "^1.1.61" } }, - "caniuse-lite": { - "version": "1.0.30001011", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001011.tgz", - "integrity": "sha512-h+Eqyn/YA6o6ZTqpS86PyRmNWOs1r54EBDcd2NTwwfsXQ8re1B38SnB+p2RKF8OUsyEIjeDU8XGec1RGO/wYCg==", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "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": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "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 + } } }, - "electron-to-chromium": { - "version": "1.3.311", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.311.tgz", - "integrity": "sha512-7GH6RKCzziLzJ9ejmbiBEdzHZsc6C3eRpav14dmRfTWMpNgMqpP1ukw/FU/Le2fR+ep642naq7a23xNdmh2s+A==", - "dev": true - }, - "inquirer": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz", - "integrity": "sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==", + "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": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" + "color-name": "1.1.3" } }, - "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=", + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "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" - } - } + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "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": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } + "has-flag": "^3.0.0" } } } }, "react-dnd": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz", - "integrity": "sha1-f6JWds+CfViokSk+PBq1naACVFo=", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz", + "integrity": "sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==", + "dev": true, "requires": { - "disposables": "^1.0.1", - "dnd-core": "^2.6.0", - "hoist-non-react-statics": "^2.1.0", - "invariant": "^2.1.0", - "lodash": "^4.2.0", - "prop-types": "^15.5.10" + "@react-dnd/shallowequal": "^2.0.0", + "@types/hoist-non-react-statics": "^3.3.1", + "dnd-core": "^11.1.3", + "hoist-non-react-statics": "^3.3.0" } }, "react-dnd-html5-backend": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz", - "integrity": "sha1-WQzRzKeEQbsnTt1XH+9MCxbdz44=", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz", + "integrity": "sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw==", + "dev": true, + "requires": { + "dnd-core": "^11.1.3" + } + }, + "react-dnd-multi-backend": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-dnd-multi-backend/-/react-dnd-multi-backend-6.0.2.tgz", + "integrity": "sha512-SwpqRv0HkJYu244FbHf9NbvGzGy14Ir9wIAhm909uvOVaHgsOq6I1THMSWSgpwUI31J3Bo5uS19tuvGpVPjzZw==", + "dev": true, + "requires": { + "dnd-multi-backend": "^6.0.0", + "prop-types": "^15.7.2", + "react-dnd-preview": "^6.0.2" + } + }, + "react-dnd-preview": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/react-dnd-preview/-/react-dnd-preview-6.0.2.tgz", + "integrity": "sha512-F2+uK4Be+q+7mZfNh9kaZols7wp1hX6G7UBTVaTpDsBpMhjFvY7/v7odxYSerSFBShh23MJl33a4XOVRFj1zoQ==", + "dev": true, + "requires": { + "prop-types": "^15.7.2" + } + }, + "react-dnd-touch-backend": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-11.1.3.tgz", + "integrity": "sha512-8lz4fxfYwUuJ6Y2seQYwh8+OfwKcbBX0CIbz7AwXfBYz54Wg2nIDU6CP8Dyybt/Wyx4D3oXmTPEaOMB62uqJvQ==", + "dev": true, "requires": { - "lodash": "^4.2.0" + "@react-dnd/invariant": "^2.0.0", + "dnd-core": "^11.1.3" } }, "react-dom": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz", - "integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz", + "integrity": "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==", + "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.18.0" + "scheduler": "^0.20.1" } }, "react-dropzone": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-4.3.0.tgz", - "integrity": "sha512-ULfrLaTSsd8BDa9KVAGCueuq1AN3L14dtMsGGqtP0UwYyjG4Vhf158f/ITSHuSPYkZXbvfcIiOlZsH+e3QWm+Q==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.2.4.tgz", + "integrity": "sha512-EGSvK2CxFTuc28WxwuJCICyuYFX8b+sRumwU6Bs6sTbElV2HtQkT0d6C+HEee6XfbjiLIZ+Th9uji27rvo2wGw==", + "dev": true, "requires": { - "attr-accept": "^1.1.3", - "prop-types": "^15.5.7" + "attr-accept": "^2.2.1", + "file-selector": "^0.2.2", + "prop-types": "^15.7.2" } }, "react-error-overlay": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.3.tgz", - "integrity": "sha512-bOUvMWFQVk5oz8Ded9Xb7WVdEi3QGLC8tH7HmYP0Fdp4Bn3qw0tRFmr5TW6mvahzvmrK4a6bqWGfCevBflP+Xw==", + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.8.tgz", + "integrity": "sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw==", "dev": true }, "react-intl": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz", - "integrity": "sha512-27jnDlb/d2A7mSJwrbOBnUgD+rPep+abmoJE511Tf8BnoONIAUehy/U1zZCHGO17mnOwMWxqN4qC0nW11cD6rA==", + "version": "5.10.6", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.10.6.tgz", + "integrity": "sha512-IWhPTGGggs/n/OKkhEHAZ7rCfQ8m/2hmYIwJtOPuNQVyKKU+R863q4xP/+uCW1NOXB+yvbF2p7CB/v2hkuEVCA==", + "dev": true, "requires": { - "hoist-non-react-statics": "^3.3.0", - "intl-format-cache": "^2.0.5", - "intl-messageformat": "^2.1.0", - "intl-relativeformat": "^2.1.0", - "invariant": "^2.1.1" - }, - "dependencies": { - "hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", - "requires": { - "react-is": "^16.7.0" - } - } + "@formatjs/ecma402-abstract": "1.5.0", + "@formatjs/intl": "1.4.10", + "@formatjs/intl-displaynames": "4.0.1", + "@formatjs/intl-listformat": "5.0.1", + "@formatjs/intl-relativetimeformat": "8.0.0", + "@types/hoist-non-react-statics": "^3.3.1", + "fast-memoize": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.3.20", + "intl-messageformat-parser": "6.0.18", + "shallow-equal": "^1.2.1", + "tslib": "^2.0.1" } }, "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true }, - "react-markdown": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-4.2.2.tgz", - "integrity": "sha512-/STJiRFmJuAIUdeBPp/VyO5bcenTIqP3LXuC3gYvregmYGKjnszGiFc2Ph0LsWC17Un3y/CT8TfxnwJT7v9EJw==", + "react-measure": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.5.2.tgz", + "integrity": "sha512-M+rpbTLWJ3FD6FXvYV6YEGvQ5tMayQ3fGrZhRPHrE9bVlBYfDCLuDcgNttYfk8IqfOI03jz6cbpqMRTUclQnaA==", + "dev": true, "requires": { - "html-to-react": "^1.3.4", - "mdast-add-list-metadata": "1.0.1", - "prop-types": "^15.7.2", - "react-is": "^16.8.6", - "remark-parse": "^5.0.0", - "unified": "^6.1.5", - "unist-util-visit": "^1.3.0", - "xtend": "^4.0.1" + "@babel/runtime": "^7.2.0", + "get-node-dimensions": "^1.2.1", + "prop-types": "^15.6.2", + "resize-observer-polyfill": "^1.5.0" } }, + "react-refresh": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz", + "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==", + "dev": true + }, "react-router": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz", - "integrity": "sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", + "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "dev": true, "requires": { "@babel/runtime": "^7.1.2", "history": "^4.9.0", "hoist-non-react-statics": "^3.1.0", "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.3.0", + "mini-create-react-context": "^0.4.0", "path-to-regexp": "^1.7.0", "prop-types": "^15.6.2", "react-is": "^16.6.0", @@ -14417,401 +40522,202 @@ "tiny-warning": "^1.0.0" }, "dependencies": { - "hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", - "requires": { - "react-is": "^16.7.0" - } - }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true }, "path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, "requires": { "isarray": "0.0.1" } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true } } }, "react-router-dom": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.1.2.tgz", - "integrity": "sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", + "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "dev": true, "requires": { "@babel/runtime": "^7.1.2", "history": "^4.9.0", "loose-envify": "^1.3.1", "prop-types": "^15.6.2", - "react-router": "5.1.2", + "react-router": "5.2.0", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" } }, "react-transition-group": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz", - "integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "dev": true, "requires": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz", - "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" - } } }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } + "react-universal-interface": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "dev": true, + "requires": {} }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "react-use": { + "version": "15.3.4", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-15.3.4.tgz", + "integrity": "sha512-cHq1dELW6122oi1+xX7lwNyE/ugZs5L902BuO8eFJCfn2api1KeuPVG1M/GJouVARoUf54S2dYFMKo5nQXdTag==", + "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "requires": { - "pinkie-promise": "^2.0.0" - } - } + "@types/js-cookie": "2.2.6", + "@xobotyi/scrollbar-width": "1.9.5", + "copy-to-clipboard": "^3.2.0", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.2.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.0.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^2.1.0", + "ts-easing": "^0.2.0", + "tslib": "^2.0.0" } }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "react-window": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.6.tgz", + "integrity": "sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==", + "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" + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" } }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" }, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, "requires": { - "ms": "2.0.0" + "pify": "^2.0.0" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } + "locate-path": "^2.0.0" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } + "p-try": "^1.0.0" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, "requires": { - "kind-of": "^6.0.0" + "p-limit": "^1.1.0" } }, - "is-data-descriptor": { + "p-try": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true }, - "is-number": { + "path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true } } }, - "realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "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": { - "util.promisify": "^1.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" } }, "recursive-readdir": { @@ -14827,121 +40733,135 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, "requires": { "indent-string": "^2.1.0", "strip-indent": "^1.0.1" + }, + "dependencies": { + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + } } }, "redux": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", - "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", + "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", + "dev": true, "requires": { - "lodash": "^4.2.1", - "lodash-es": "^4.2.1", - "loose-envify": "^1.1.0", - "symbol-observable": "^1.0.3" + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" } }, "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" + "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": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, "requires": { "regenerate": "^1.4.0" } }, "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true }, "regenerator-transform": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", - "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==", - "requires": { - "private": "^0.1.6" - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", "dev": true, "requires": { - "is-equal-shallow": "^0.1.3" + "@babel/runtime": "^7.8.4" } }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, "requires": { "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexp.prototype.flags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", - "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", - "requires": { - "define-properties": "^1.1.2" - } - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" + "safe-regex": "^1.1.0" } }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", "dev": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } } }, - "registry-url": { + "regexpp": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", "dev": true, "requires": { - "rc": "^1.0.1" + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" } }, "regjsgen": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==" + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true }, "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, "requires": { "jsesc": "~0.5.0" }, @@ -14949,81 +40869,79 @@ "jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + "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=" - }, - "remark-parse": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", - "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", - "requires": { - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^1.1.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^1.0.0", - "vfile-location": "^2.0.0", - "xtend": "^4.0.1" - } + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true }, "renderkid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", - "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.4.tgz", + "integrity": "sha512-K2eXrSOJdq+HuKzlcjOlGoOarUu5SDguDEhE7+Ah4zuOWL40j8A/oHvLlLob9PSTNvVnBd+/q0Er1QfpEuem5g==", + "dev": true, "requires": { "css-select": "^1.1.0", "dom-converter": "^0.2", "htmlparser2": "^3.3.0", - "strip-ansi": "^3.0.0", - "utila": "^0.4.0" + "lodash": "^4.17.20", + "strip-ansi": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true }, "repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, "requires": { "is-finite": "^1.0.0" } }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" - }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -15032,7 +40950,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -15042,52 +40960,101 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" }, "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "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.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true } } }, "request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", "dev": true, "requires": { - "lodash": "^4.17.11" + "lodash": "^4.17.19" } }, "request-promise-native": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", - "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", "dev": true, "requires": { - "request-promise-core": "1.1.2", + "request-promise-core": "1.1.4", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" + }, + "dependencies": { + "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 + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-from-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", - "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "requires-port": { "version": "1.0.0", @@ -15095,111 +41062,68 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, - "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "dev": true }, "resolve": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz", - "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, "requires": { + "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "^5.0.0" }, "dependencies": { "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true } } }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "dependencies": { - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - } - } - }, "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==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true }, "resolve-pathname": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", - "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==", + "dev": true }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true }, "ress": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ress/-/ress-1.2.2.tgz", - "integrity": "sha1-T9AGgcjTzqp59d4eVKqtFWF+l3c=" - }, - "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" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ress/-/ress-3.0.0.tgz", + "integrity": "sha512-MTPto7t44AawqmSbEmvMKoSMWPnxjaTuHf94s7RjWxuSGFN0o8/b+6yOwkaC50+Vihjsu6ODUEQR397gTMn57w==", + "dev": true }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true }, "retry": { "version": "0.12.0", @@ -15207,18 +41131,29 @@ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "dev": true }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "requires": { - "align-text": "^0.1.1" - } + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "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" } @@ -15234,51 +41169,43 @@ } }, "rsvp": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", - "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", "dev": true }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "rtl-css-js": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.14.0.tgz", + "integrity": "sha512-Dl5xDTeN3e7scU1cWX8c9b6/Nqz3u/HgR4gePc1kWXYiQWVQbKCEyK6+Hxve9LbcJ5EieHy1J9nJCN3grTtGwg==", "dev": true, "requires": { - "is-promise": "^2.1.0" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "requires": { - "aproba": "^1.1.1" + "@babel/runtime": "^7.1.2" } }, - "run-series": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.8.tgz", - "integrity": "sha512-+GztYEPRpIsQoCSraWHDBs9WVy4eVME16zhOtDB4H9J4xN0XRhknnmLOl+4gRgZtu8dpp9N/utSPjKH/xmDzXg==" + "run-parallel": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", + "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", + "dev": true }, - "rxjs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", - "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } + "rusha": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.13.tgz", + "integrity": "sha1-mghOe4YLF7/zAVuSxnpqM2GRUTo=", + "dev": true }, "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==" + "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 }, "safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, "requires": { "ret": "~0.1.10" } @@ -15289,20 +41216,20 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sane": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/sane/-/sane-2.5.2.tgz", - "integrity": "sha1-tNwYYcIbQn6SlQej51HiosuKs/o=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", "dev": true, "requires": { + "@cnakazawa/watch": "^1.0.3", "anymatch": "^2.0.0", - "capture-exit": "^1.2.0", - "exec-sh": "^0.2.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", "fb-watchman": "^2.0.0", - "fsevents": "^1.2.3", "micromatch": "^3.1.4", "minimist": "^1.1.1", - "walker": "~1.0.5", - "watch": "~0.18.0" + "walker": "~1.0.5" }, "dependencies": { "anymatch": { @@ -15315,173 +41242,24 @@ "normalize-path": "^2.1.1" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "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" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { + "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", "to-regex": "^3.0.1" }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -15493,6 +41271,34 @@ } } }, + "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" + } + }, + "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" + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -15516,34 +41322,20 @@ } } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "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": { - "kind-of": "^6.0.0" + "pump": "^3.0.0" } }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true }, "is-number": { "version": "3.0.0", @@ -15565,16 +41357,10 @@ } } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "micromatch": { @@ -15598,94 +41384,355 @@ "to-regex": "^3.0.2" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "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" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "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 + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "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" + } } } }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, "sass-graph": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", - "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", + "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", + "dev": true, "requires": { "glob": "^7.0.0", "lodash": "^4.0.0", "scss-tokenizer": "^0.2.3", - "yargs": "^7.0.0" + "yargs": "^13.3.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "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" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.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 + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.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 + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } } }, "sass-loader": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", - "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.1.0.tgz", + "integrity": "sha512-ZCKAlczLBbFd3aGAhowpYEy69Te3Z68cg8bnHHl6WnSCvnKpbM6pQrz957HWMa8LKVuhnD9uMplmMAHwGQtHeg==", + "dev": true, "requires": { - "clone-deep": "^4.0.1", - "loader-utils": "^1.0.1", - "neo-async": "^2.5.0", - "pify": "^4.0.1", - "semver": "^6.3.0" + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" }, "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true }, "saxen": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/saxen/-/saxen-8.1.0.tgz", - "integrity": "sha512-34U5SdDUxECB5Jkwbc2mAdxHyGvbfCHv0iHgf+x2jaYLlwsPpju9651Lld9CpFpF4zJsoWcF3Q05blXXNOb/cg==" + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/saxen/-/saxen-8.1.2.tgz", + "integrity": "sha512-xUOiiFbc3Ow7p8KMxwsGICPx46ZQvy3+qfNVhrkwfz3Vvq45eGt98Ft5IQaA1R/7Tb5B5MKh9fUR9x3c3nDTxw==", + "dev": true + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } }, "scheduler": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", - "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.1.tgz", + "integrity": "sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==", + "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "schema-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz", - "integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==", + "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": { - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1" - }, - "dependencies": { - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==" - } + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" } }, + "screenfull": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.0.2.tgz", + "integrity": "sha512-cCF2b+L/mnEiORLN5xSAz6H3t18i2oHh9BA8+CQlAh5DRw2+NFAGQJOSYbcGw8B2k04g/lVvFcfZ83b3ysH5UQ==", + "dev": true + }, "scss-tokenizer": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, "requires": { "js-base64": "^2.1.8", "source-map": "^0.4.2" @@ -15695,6 +41742,7 @@ "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, "requires": { "amdefine": ">=0.0.4" } @@ -15704,40 +41752,35 @@ "select": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", + "dev": true }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true }, "selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", + "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", "dev": true, "requires": { - "node-forge": "0.9.0" + "node-forge": "^0.10.0" } }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" - }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "dev": true, - "requires": { - "semver": "^5.0.3" - } + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, "requires": { "debug": "2.6.9", "depd": "~1.1.2", @@ -15758,6 +41801,7 @@ "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" }, @@ -15765,16 +41809,47 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } + }, + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serialize-query-params": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/serialize-query-params/-/serialize-query-params-1.2.4.tgz", + "integrity": "sha512-m4hGkOY5y+ksPDSEkw12cNxt3HRUJv5G6oF9/4yq+GCw4LznudxC73qnz++VTHqXa0j1x1/iaBIpoiMBxr6w2w==", + "dev": true, + "requires": {} }, "serve-index": { "version": "1.9.1", @@ -15812,6 +41887,12 @@ "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", @@ -15830,6 +41911,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -15840,12 +41922,20 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-harmonic-interval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz", + "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==", + "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -15857,21 +41947,30 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "^0.1.0" } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true } } }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true }, "sha.js": { "version": "2.4.11", @@ -15883,33 +41982,26 @@ "safe-buffer": "^5.0.1" } }, - "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==", - "requires": { - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } + "shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", + "dev": true }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "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": "^1.0.0" + "shebang-regex": "^3.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=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true }, "shell-quote": { "version": "1.7.2", @@ -15921,17 +42013,62 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true + }, + "side-channel": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", + "dev": true, + "requires": { + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" + } }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "simple-sha1": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/simple-sha1/-/simple-sha1-3.0.1.tgz", + "integrity": "sha512-q7ehqWfHc1VhOm7sW099YDZ4I0yYX7rqyhqqhHV1IYeUTjPOhHyD3mXvv8k2P+rO7+7c8R4/D+8ffzC9BE7Cqg==", + "dev": true, + "requires": { + "queue-microtask": "^1.1.2", + "rusha": "^0.8.1" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true }, "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "slice-ansi": { @@ -15945,6 +42082,30 @@ "is-fullwidth-code-point": "^2.0.0" }, "dependencies": { + "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" + } + }, + "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 + }, "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", @@ -15957,6 +42118,7 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -15972,6 +42134,7 @@ "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" } @@ -15980,6 +42143,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -15988,14 +42152,79 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, "requires": { "is-extendable": "^0.1.0" } }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, @@ -16003,6 +42232,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -16013,45 +42243,10 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, "requires": { "is-descriptor": "^1.0.0" } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -16059,70 +42254,138 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, "requires": { "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", + "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", "dev": true, "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" + "faye-websocket": "^0.11.3", + "uuid": "^3.4.0", + "websocket-driver": "^0.7.4" }, "dependencies": { - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true } } }, "sockjs-client": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", - "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.0.tgz", + "integrity": "sha512-8Dt3BDi4FYNrCFGTL/HtwVzkARrENdwOUf1ZoW/9p3M8lZdFT35jVdrHza+qgxuG9H3/shR4cuX/X9umUrjP8Q==", "dev": true, "requires": { - "debug": "^3.2.5", + "debug": "^3.2.6", "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" + "faye-websocket": "^0.11.3", + "inherits": "^2.0.4", + "json3": "^3.3.3", + "url-parse": "^1.4.7" + }, + "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" + } + } } }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true }, "source-map-loader": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", - "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-1.1.3.tgz", + "integrity": "sha512-6YHeF+XzDOrT/ycFJNI53cgEsp/tHTMl37hi7uVyqFAlTXW109JazaQCkbc+jjoL2637qkH1amLi+JzrIpt5lA==", + "dev": true, "requires": { - "async": "^2.5.0", - "loader-utils": "^1.1.0" + "abab": "^2.0.5", + "iconv-lite": "^0.6.2", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "source-map": "^0.6.1", + "whatwg-mimetype": "^2.3.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, "requires": { - "atob": "^2.1.1", + "atob": "^2.1.2", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", "source-map-url": "^0.4.0", @@ -16130,121 +42393,119 @@ } }, "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { - "source-map": "^0.5.6" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true }, "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "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.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + "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.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "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.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true }, "spdy": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", - "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", + "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": "^2.6.8", - "handle-thing": "^1.2.5", + "debug": "^4.1.0", + "handle-thing": "^2.0.0", "http-deceiver": "^1.2.7", - "safe-buffer": "^5.0.1", "select-hose": "^2.0.0", - "spdy-transport": "^2.0.18" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "spdy-transport": "^3.0.0" } }, "spdy-transport": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.1.tgz", - "integrity": "sha512-q7D8c148escoB3Z7ySCASadkegMmUZW8Wb/Q1u0/XBgDKMO880rLQDj8Twiew/tYi7ghemKUi/whSYOwE17f5Q==", + "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": "^2.6.8", - "detect-node": "^2.0.3", + "debug": "^4.1.0", + "detect-node": "^2.0.4", "hpack.js": "^2.1.6", - "obuf": "^1.1.1", - "readable-stream": "^2.2.9", - "safe-buffer": "^5.0.1", - "wbuf": "^1.7.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" } }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "dev": true + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, "requires": { "extend-shallow": "^3.0.0" } }, "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -16260,517 +42521,123 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true } } }, "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stack-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", - "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", - "dev": true - }, - "state-toggle": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.2.tgz", - "integrity": "sha512-8LpelPGR0qQM4PnfLiplOQNJcIN1/r2Gy0xKB2zKnIW2YzPMt2sR4I/+gtPjhN7Svh9kw+zqEg2SFwpBO9iNiw==" - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", - "requires": { - "readable-stream": "^2.0.1" - } - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" - }, - "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", + "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", "dev": true, "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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" - } - } + "minipass": "^3.1.1" } }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true }, - "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "stack-generator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.5.tgz", + "integrity": "sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "stackframe": "^1.1.1" } }, - "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.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==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "requires": { - "get-stdin": "^4.0.1" - }, - "dependencies": { - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" - } + "escape-string-regexp": "^2.0.0" } }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "stackframe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", + "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", "dev": true }, - "style-loader": { - "version": "0.20.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", - "integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==", + "stacktrace-gps": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz", + "integrity": "sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg==", + "dev": true, "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^0.4.5" + "source-map": "0.5.6", + "stackframe": "^1.1.1" }, "dependencies": { - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "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==" - }, - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true } } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dev": true, "requires": { - "has-flag": "^3.0.0" + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" } }, - "svg-baker": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/svg-baker/-/svg-baker-1.4.1.tgz", - "integrity": "sha512-Y14nrVtN8IMYZ5K8yNlepdGwfAi0GClqaoSMTtYsQxFfaobtyDTM816lTWMA5tbbXDbih1frv7AGil54dtX6YA==", - "requires": { - "bluebird": "^3.5.0", - "clone": "^2.1.1", - "he": "^1.1.1", - "image-size": "^0.5.1", - "loader-utils": "^1.1.0", - "merge-options": "1.0.1", - "micromatch": "3.1.0", - "postcss": "^5.2.17", - "postcss-prefix-selector": "^1.6.0", - "posthtml-rename-id": "^1.0", - "posthtml-svg-mode": "^1.0.3", - "query-string": "^4.3.2", - "traverse": "^0.6.6" + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - } - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "is-descriptor": "^0.1.0" } }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" - }, "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } } } }, "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -16779,2799 +42646,1965 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } } } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - }, - "micromatch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.0.tgz", - "integrity": "sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.2.2", - "define-property": "^1.0.0", - "extend-shallow": "^2.0.1", - "extglob": "^2.0.2", - "fragment-cache": "^0.2.1", - "kind-of": "^5.0.2", - "nanomatch": "^1.2.1", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - } - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "requires": { - "has-flag": "^1.0.0" - } + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true } } }, - "svg-baker-runtime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/svg-baker-runtime/-/svg-baker-runtime-1.4.1.tgz", - "integrity": "sha512-4HLs8dU5kP4+zWix3MoZUge7kSVO6VCg14dLJWVIRWZx1vik21k/h5T620VXRoIyn0nw8g1R8Qk+VvW9BLOOPA==", - "requires": { - "deepmerge": "1.3.2", - "mitt": "1.1.2", - "svg-baker": "^1.4.0" - } + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true }, - "svg-sprite-loader": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/svg-sprite-loader/-/svg-sprite-loader-4.1.6.tgz", - "integrity": "sha512-fVLDJ36GUfLmeZEB+RFq/OmiEThifZsQQ+8YSmbpOtyvlkLBbnGydYG6IHJehgbpy58898JyoHk+krd6C+6dXg==", - "requires": { - "bluebird": "^3.5.0", - "deepmerge": "1.3.2", - "domready": "1.0.8", - "escape-string-regexp": "1.0.5", - "html-webpack-plugin": "^3.2.0", - "loader-utils": "^1.1.0", - "svg-baker": "^1.4.1", - "svg-baker-runtime": "1.4.1", - "url-slug": "2.0.0" - }, - "dependencies": { - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" - }, - "html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", - "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", - "requires": { - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "tapable": "^1.0.0", - "toposort": "^1.0.0", - "util.promisify": "1.0.0" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - } - } - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + "std-env": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.2.1.tgz", + "integrity": "sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ==", + "dev": true, + "requires": { + "ci-info": "^1.6.0" + }, + "dependencies": { + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true } } }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, - "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", - "dev": true - }, - "table": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.1.tgz", - "integrity": "sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w==", + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", "dev": true, "requires": { - "ajv": "^6.9.1", - "lodash": "^4.17.11", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "readable-stream": "^2.0.1" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "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 - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "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": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "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" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "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 + }, + "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": { - "ansi-regex": "^4.1.0" + "safe-buffer": "~5.1.0" } } } }, - "taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "dev": true }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" - }, - "tar": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - } - }, - "tar-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", - "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", - "requires": { - "bl": "^1.0.0", - "buffer-alloc": "^1.2.0", - "end-of-stream": "^1.0.0", - "fs-constants": "^1.0.0", - "readable-stream": "^2.3.0", - "to-buffer": "^1.1.1", - "xtend": "^4.0.0" - } - }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", "dev": true, "requires": { - "execa": "^0.7.0" - } - }, - "terser": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.0.tgz", - "integrity": "sha512-oDG16n2WKm27JO8h4y/w3iqBGAOSCtq7k8dRmrn4Wf9NouL0b2WpMHGChFGZq4nFAQy1FsNJrVQHfurXOSTmOA==", - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "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" + } + }, + "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 }, - "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "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": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "safe-buffer": "~5.1.0" } } } }, - "terser-webpack-plugin": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", - "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^1.7.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" }, "dependencies": { - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "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": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "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" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "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 + }, + "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" + } } } }, - "test-exclude": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.3.tgz", - "integrity": "sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "micromatch": "^2.3.11", - "object-assign": "^4.1.0", - "read-pkg-up": "^1.0.1", - "require-main-filename": "^1.0.1" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=", "dev": true }, - "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "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": { - "setimmediate": "^1.0.4" + "safe-buffer": "~5.2.0" } }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, - "tiny-invariant": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.4.tgz", - "integrity": "sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g==" - }, - "tiny-warning": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.2.tgz", - "integrity": "sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q==" - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "string-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", + "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" } }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", "dev": true }, - "to-buffer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" - }, - "to-camel-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-camel-case/-/to-camel-case-1.0.0.tgz", - "integrity": "sha1-GlYFSy+daWKYzmamCJcyK29CPkY=", - "requires": { - "to-space-case": "^1.0.0" - } - }, - "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=" - }, - "to-no-case": { + "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", - "integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo=" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - } - } - } - }, - "to-space-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", - "integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=", - "requires": { - "to-no-case": "^1.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "token-stream": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", - "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" - }, - "topo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", - "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", - "requires": { - "hoek": "6.x.x" - }, - "dependencies": { - "hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" - } - } - }, - "toposort": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", - "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=" - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "nopt": "~1.0.10" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" }, "dependencies": { - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "abbrev": "1" + "ansi-regex": "^2.0.0" } } } }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } + "string.prototype.matchall": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz", + "integrity": "sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has-symbols": "^1.0.1", + "internal-slot": "^1.0.2", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.3" } }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", "dev": true, "requires": { - "punycode": "^2.1.0" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, - "traverse": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", - "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" - }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" - }, - "trim-trailing-lines": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.2.tgz", - "integrity": "sha512-MUjYItdrqqj2zpcHFTkMa9WAv4JHTI6gnRQGPFLrt5L9a6tRMiDnIqYl8JBvu2d2Tc3lWJKQwlGCp0K8AvCM+Q==" - }, - "trough": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.4.tgz", - "integrity": "sha512-tdzBRDGWcI1OpPVmChbdSKhvSVurznZ8X36AYURAcl+0o2ldlCY2XPzyXNNxwJwwyIU+rIglTCG4kxtNKBQH7Q==" - }, - "true-case-path": { + "string.prototype.trimstart": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "dev": true, "requires": { - "glob": "^7.1.2" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", - "dev": true - }, - "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "tslib": "^1.8.1" + "ansi-regex": "^5.0.0" } }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^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 }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "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 }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "get-stdin": "^4.0.1" } }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true }, - "typed-css-modules": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/typed-css-modules/-/typed-css-modules-0.5.1.tgz", - "integrity": "sha512-7wt3NfhrPXEnUguk2qv4YdL1k/G/AI4we9wkbQ2F+ZmMNH2xISMvNR8W46GiJEVE0t4RZHDnxDQGK2RhK4P0Wg==", + "style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "dev": true, "requires": { - "@types/css-modules-loader-core": "^1.1.0", - "camelcase": "^5.3.1", - "chalk": "^2.1.0", - "chokidar": "^2.1.2", - "css-modules-loader-core": "^1.1.0", - "glob": "^7.1.2", - "is-there": "^4.4.2", - "mkdirp": "^0.5.1", - "yargs": "^8.0.2" + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "anymatch": { + "loader-utils": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "chokidar": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", - "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { - "locate-path": "^2.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, "requires": { - "kind-of": "^6.0.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "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=" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + } + } + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "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": { - "is-extglob": "^2.1.1" + "color-convert": "^1.9.0" } }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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": { - "kind-of": "^3.0.2" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "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": { - "is-buffer": "^1.1.5" + "has-flag": "^3.0.0" } } } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "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" + } }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" } }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" } }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "has-flag": "^3.0.0" } + } + } + }, + "stylis": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.0.tgz", + "integrity": "sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw==", + "dev": true + }, + "superagent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", + "dev": true, + "requires": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "dependencies": { + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "supertest": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.0.1.tgz", + "integrity": "sha512-8yDNdm+bbAN/jeDdXsRipbq9qMpVF7wRsbwLgsANHqdjPsCoecmlTuqEcLQMGpmojFBhxayZ0ckXmLXYq7e+0g==", + "dev": true, + "requires": { + "methods": "1.1.2", + "superagent": "6.1.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" + } + }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "dependencies": { + "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": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "color-convert": "^1.9.0" } }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "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": { - "p-try": "^1.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "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": { - "p-limit": "^1.1.0" + "color-name": "1.1.3" } }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, "requires": { - "pify": "^2.0.0" + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" } }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" + "mdn-data": "2.0.4", + "source-map": "^0.6.1" } }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "dev": true + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "dom-serializer": "0", + "domelementtype": "1" } }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "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 + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "minimist": "^1.2.5" } }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "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": { - "ansi-regex": "^3.0.0" + "has-flag": "^3.0.0" } + } + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, - "which-module": { + "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, - "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - } + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { - "camelcase": "^4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - } + "ansi-regex": "^4.1.0" } } } }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "typescript": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz", - "integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==", + "tapable": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", "dev": true }, - "ua-parser-js": { - "version": "0.7.19", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz", - "integrity": "sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==" + "tar": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", + "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } }, - "uglify-js": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", - "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, "requires": { - "commander": "~2.19.0", - "source-map": "~0.6.1" + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" }, "dependencies": { "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + "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 }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "optional": true - }, - "undefsafe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", - "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", + "terser-webpack-plugin": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.0.3.tgz", + "integrity": "sha512-zFdGk8Lh9ZJGPxxPE6jwysOlATWB8GMW8HcfGULWA/nPal+3VdATflQvSBSLQJRCmYZnfFJl6vkRTiwJGNgPiQ==", "dev": true, "requires": { - "debug": "^2.2.0" + "jest-worker": "^26.6.1", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.3.8" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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 + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "requires": { - "ms": "2.0.0" + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true + }, + "terser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.5.1.tgz", + "integrity": "sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "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 + } + } } } }, - "underscore": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", - "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" - }, - "unherit": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.2.tgz", - "integrity": "sha512-W3tMnpaMG7ZY6xe/moK04U9fBhi6wEiCYHUW5Mop/wQHf12+79EQGwxYejNdhEz2mkqkBlGwm7pxmgBKMVUj0w==", + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, "requires": { - "inherits": "^2.0.1", - "xtend": "^4.0.1" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true }, - "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==" + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true }, - "unicode-property-aliases-ecmascript": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", - "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==" + "throttle-debounce": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", + "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==", + "dev": true }, - "unidecode": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/unidecode/-/unidecode-0.1.8.tgz", - "integrity": "sha1-77swFTi8RSRqmsjFWdcvAVMFBT4=" + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true }, - "unified": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", - "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^1.1.0", - "trough": "^1.0.0", - "vfile": "^2.0.0", - "x-is-string": "^0.1.0" + "setimmediate": "^1.0.4" } }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } - } + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "dev": true }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "requires": { - "unique-slug": "^2.0.0" - } + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==", + "dev": true }, - "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "requires": { - "imurmurhash": "^0.1.4" - } + "tiny-typed-emitter": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.0.3.tgz", + "integrity": "sha512-MaCqhHlp6EAWN25yqBlajgd4scxxI2eJr7+EgoUAOV9UkMU3us/yp2bEnc2yOvyeDF8TUWuaz3zZCPGTKFJIpA==", + "dev": true }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", - "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" - } + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true }, - "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==" + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true }, - "unist-util-remove-position": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", - "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", - "requires": { - "unist-util-visit": "^1.1.0" - } + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true }, - "unist-util-stringify-position": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", - "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==" + "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 }, - "unist-util-visit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", - "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, "requires": { - "unist-util-visit-parents": "^2.0.0" + "kind-of": "^3.0.2" }, "dependencies": { - "unist-util-visit-parents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", - "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { - "unist-util-is": "^3.0.0" + "is-buffer": "^1.1.5" } } } }, - "unist-util-visit-parents": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz", - "integrity": "sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q==" - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } - } - }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, - "upath": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", - "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } }, - "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "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": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "is-number": "^7.0.0" } }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=", + "dev": true }, - "upper-case-first": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", - "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=", - "requires": { - "upper-case": "^1.1.1" - } + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, "requires": { - "punycode": "^2.1.0" + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "dependencies": { + "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 + } } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "tr46": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", + "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", "dev": true, "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "punycode": "^2.1.1" }, "dependencies": { "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "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 } } }, - "url-loader": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz", - "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "requires": { + "glob": "^7.1.2" + } + }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "dev": true, "requires": { - "loader-utils": "^1.2.3", - "mime": "^2.4.4", - "schema-utils": "^2.5.0" + "utf8-byte-length": "^1.0.1" + } + }, + "ts-easing": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", + "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==", + "dev": true + }, + "ts-jest": { + "version": "26.4.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.4.4.tgz", + "integrity": "sha512-3lFWKbLxJm34QxyVNNCgXX1u4o/RV0myvA2y2Bxm46iGIjKlaY0own9gIckbjZJPn+WaJEnfPPJ20HHGpoq4yg==", + "dev": true, + "requires": { + "@types/jest": "26.x", + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^26.1.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "mkdirp": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" }, "dependencies": { - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, - "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", "dev": true, "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" } }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "ts-node-dev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.1.tgz", + "integrity": "sha512-kAO8LUZgXZSY0+PucMPsQ0Bbdv0x+lgbN7j8gcD4PuTI4uKC6YchekaspmYTBNilkiu+rQYkWJA7cK+Q8/B0tQ==", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "chokidar": "^3.4.0", + "dateformat": "~1.0.4-1.2.3", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^9.0.0", + "tsconfig": "^7.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, - "url-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/url-slug/-/url-slug-2.0.0.tgz", - "integrity": "sha1-p4nVrtSZXA2VrzM3etHVxo1NcCc=", + "tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, "requires": { - "unidecode": "0.1.8" + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } } }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", "dev": true, "requires": { - "inherits": "2.0.3" + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } } }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } }, - "v8-compile-cache": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "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==", + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "prelude-ls": "^1.2.1" } }, - "value-equal": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", - "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "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": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" } }, - "vfile": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", - "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "typed-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-1.3.1.tgz", + "integrity": "sha512-2h7utWyXgd2R2u2IuL8B4yu1gqMxbgUj2VS/MGVbFhEVQNJKXoQQoS5CBMh+eW31zFeSmDfEQ3qQf4xy5SlPVQ==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, "requires": { - "is-buffer": "^1.1.4", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "^1.0.0", - "vfile-message": "^1.0.0" + "is-typedarray": "^1.0.0" } }, - "vfile-location": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", - "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==" + "typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "dev": true }, - "vfile-message": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", - "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", - "requires": { - "unist-util-stringify-position": "^1.1.1" - } + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "underscore": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", + "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", "dev": true, "requires": { - "indexof": "0.0.1" + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" } }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true }, - "w3c-hr-time": { + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "union-value": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { - "browser-process-hrtime": "^0.1.2" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + } } }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true }, - "watch": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz", - "integrity": "sha1-KAlUdsbffJDJYxOJkMClQj60uYY=", + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { - "exec-sh": "^0.2.0", - "minimist": "^1.2.0" + "unique-slug": "^2.0.0" } }, - "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "dev": true, "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" + "imurmurhash": "^0.1.4" } }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "requires": { - "minimalistic-assert": "^1.0.0" - } + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, - "webpack": { - "version": "4.41.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz", - "integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==", + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.2.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.1", - "watchpack": "^1.6.0", - "webpack-sources": "^1.4.1" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "dependencies": { - "acorn": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", - "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", - "dev": true - }, - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", - "dev": true - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "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" - } - }, - "enhanced-resolve": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", - "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "isarray": "1.0.0" } } } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "dev": true - }, - "node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - } - }, - "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", "dev": true - }, + } + } + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "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 } } }, - "webpack-cli": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.10.tgz", - "integrity": "sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg==", + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", "dev": true, "requires": { - "chalk": "2.4.2", - "cross-spawn": "6.0.5", - "enhanced-resolve": "4.1.0", - "findup-sync": "3.0.0", - "global-modules": "2.0.0", - "import-local": "2.0.0", - "interpret": "1.2.0", - "loader-utils": "1.2.3", - "supports-color": "6.1.0", - "v8-compile-cache": "2.0.3", - "yargs": "13.2.4" + "punycode": "1.3.2", + "querystring": "0.2.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.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" - } - }, - "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" - } - }, - "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" - } - }, - "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-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" - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "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 - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "require-main-filename": { + } + } + }, + "url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + } + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "use-query-params": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/use-query-params/-/use-query-params-1.1.9.tgz", + "integrity": "sha512-WAJ1GrKbFWv1TBn1RQpHqAwC7yyJsLaJjBhIfefrbY/h6mFSngzBQKirJndYwCS1ry77EwhpR/tQi5iovXWvuw==", + "dev": true, + "requires": { + "serialize-query-params": "^1.2.3" + } + }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true - }, - "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, - "webpack-dev-middleware": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", "dev": true, "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", - "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" }, "dependencies": { - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "dev": true, "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", - "dev": true } } }, - "webpack-dev-server": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz", - "integrity": "sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==", + "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.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", + "dev": true, + "optional": true + }, + "v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "dev": true + }, + "v8-to-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.0.0.tgz", + "integrity": "sha512-fLL2rFuQpMtm9r8hrAV2apXX/WqHJ6+IC4/eQVdMDGBUgH/YMV4Gv3duk3kjmyg6uiQWBAA9nJwue4iJUOkHeA==", "dev": true, "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.2.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.4", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.25", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.7", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "0.3.19", - "sockjs-client": "1.4.0", - "spdy": "^4.0.1", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "12.0.5" + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "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" - } - } - } - }, - "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 - } - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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" - } - }, - "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" - } - }, - "handle-thing": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", - "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", - "dev": true - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "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=", + "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 - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + } + } + }, + "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" + } + }, + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "watchpack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.0.tgz", + "integrity": "sha512-UjgD1mqjkG99+3lgG36at4wPnUXNvis2v1utwTgQ43C22c4LD71LsYMExdWXh4HZ+RmW+B0t1Vrg2GpXAkTOQw==", + "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" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "webpack": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.10.1.tgz", + "integrity": "sha512-mHu4iM2mW7d/8R91VPPNtUCNd1D8k51TTb4e0XjylapIR6WEmW8XUTBZq8TqmShj9XYxVXJn6AzKlWnrlty6DA==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.45", + "@webassemblyjs/ast": "1.9.1", + "@webassemblyjs/helper-module-context": "1.9.1", + "@webassemblyjs/wasm-edit": "1.9.1", + "@webassemblyjs/wasm-parser": "1.9.1", + "acorn": "^8.0.4", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.3.1", + "eslint-scope": "^5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.1.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "pkg-dir": "^5.0.0", + "schema-utils": "^3.0.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.0.3", + "watchpack": "^2.0.0", + "webpack-sources": "^2.1.1" + }, + "dependencies": { + "acorn": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.4.tgz", + "integrity": "sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ==", "dev": true }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "enhanced-resolve": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.4.0.tgz", + "integrity": "sha512-ZmqfWURB2lConOBM1JdCVfPyMRv5RdKWktLXO6123p97ovVm2CLBgw9t5MBj3jJWA6eHyOeIws9iJQoGFR4euQ==", "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "graceful-fs": "^4.2.4", + "tapable": "^2.0.0" } }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "spdy": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", - "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "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" + "p-locate": "^5.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==", + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "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" + "p-limit": "^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==", + "pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "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" - } - } + "find-up": "^5.0.0" } }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } + "tapable": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", + "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", + "dev": true }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "webpack-sources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", + "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", "dev": true, "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" } } } }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "webpack-dev-middleware": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-4.0.2.tgz", + "integrity": "sha512-xyAICqIugWtT1RRH5aMMmZlPhDhEqPTDL0TWhmMZsuZ+cFlAvRxv4thCbuxdk9MW+OYK4c9BkfmgdQ1/7imkJA==", "dev": true, "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" + "mem": "^8.0.0", + "memfs": "^3.2.0", + "mime-types": "^2.1.27", + "range-parser": "^1.2.1", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } } }, - "webpack-manifest-plugin": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz", - "integrity": "sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ==", + "webpack-dev-server": { + "version": "4.0.0-beta.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.0.0-beta.0.tgz", + "integrity": "sha512-mVD4Hn3bsMdcq6qE0y8xvH6KAu9NwS6F0NNgFe+n6gbsTQ7YgffUDydvy2iieyyKjAcBJDT5PZexv9tKv8kTNQ==", "dev": true, "requires": { - "fs-extra": "^7.0.0", - "lodash": ">=3.5 <5", - "object.entries": "^1.1.0", - "tapable": "^1.0.0" + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^3.4.3", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "del": "^6.0.0", + "express": "^4.17.1", + "find-cache-dir": "^3.3.1", + "graceful-fs": "^4.2.4", + "html-entities": "^1.3.1", + "http-proxy-middleware": "^1.0.6", + "internal-ip": "^6.2.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "open": "^7.3.0", + "p-retry": "^4.2.0", + "portfinder": "^1.0.28", + "schema-utils": "^3.0.0", + "selfsigned": "^1.10.8", + "serve-index": "^1.9.1", + "sockjs": "0.3.21", + "sockjs-client": "1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^6.0.0", + "url": "^0.11.0", + "util": "^0.12.3", + "webpack-dev-middleware": "^4.0.2", + "ws": "^7.4.0" }, "dependencies": { - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "util": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", + "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" } } } @@ -19580,6 +44613,7 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, "requires": { "source-list-map": "^2.0.0", "source-map": "~0.6.1" @@ -19588,25 +44622,42 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, + "webpackbar": { + "version": "5.0.0-3", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz", + "integrity": "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.1.0", + "consola": "^2.15.0", + "figures": "^3.2.0", + "pretty-time": "^1.1.0", + "std-env": "^2.2.1", + "text-table": "^0.2.0", + "wrap-ansi": "^7.0.0" + } + }, "websocket-driver": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", - "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "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.4.0 <0.4.11", + "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "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-encoding": { @@ -19618,11 +44669,6 @@ "iconv-lite": "0.4.24" } }, - "whatwg-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" - }, "whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", @@ -19630,168 +44676,129 @@ "dev": true }, "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz", + "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" + "tr46": "^2.0.2", + "webidl-conversions": "^6.1.0" } }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "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" } }, "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + } }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, "requires": { "string-width": "^1.0.2 || 2" } }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "worker-rpc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", + "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", + "dev": true, + "requires": { + "microevent.ts": "~0.1.1" + } + }, + "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": { - "string-width": "^2.1.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "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 }, "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=", + "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 }, "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=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } } } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" - }, - "with": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", - "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", - "requires": { - "acorn": "^3.1.0", - "acorn-globals": "^3.0.0" - } - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" - }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "requires": { - "errno": "~0.1.7" - } - }, - "worker-rpc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", - "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", - "dev": true, - "requires": { - "microevent.ts": "~0.1.1" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, "write-file-atomic": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", - "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", + "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==", "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, - "x-is-string": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", - "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" - }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", - "dev": true + "requires": {} }, "xml-name-validator": { "version": "3.0.0", @@ -19802,79 +44809,94 @@ "xmlbuilder": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", - "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=" + "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=", + "dev": true }, - "xmlcreate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.1.tgz", - "integrity": "sha512-MjGsXhKG8YjTKrDCXseFo3ClbMGvUD4en29H2Cev1dv4P/chlpw6KdYmlCWDkhosBVKRDjM836+3e3pm1cBNJA==", + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, "xmlrpc": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/xmlrpc/-/xmlrpc-1.3.2.tgz", "integrity": "sha1-JrLqNHhI0Ciqx+dRS1NRl23j6D0=", + "dev": true, "requires": { "sax": "1.2.x", "xmlbuilder": "8.2.x" } }, "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", + "dev": true }, "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "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.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true }, "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", + "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", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "dependencies": { - "camelcase": { + "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 + }, + "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + "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 + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } } } }, "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "requires": { - "camelcase": "^3.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" - } - } + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true }, "yauzl": { "version": "2.10.0", @@ -19884,6 +44906,24 @@ "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, + "zod": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/zod/-/zod-1.11.11.tgz", + "integrity": "sha512-q1YeBpu+c7eUX5fDFMyfP97sD74TUQ+UN8va/nvbxnArr5euYsNO6fjiY0SdDkHKNZ+xBR2ZQToaeLgJ6fsB2A==", + "dev": true } } } diff --git a/package.json b/package.json index 6d2681b0a..f52822fbb 100644 --- a/package.json +++ b/package.json @@ -1,143 +1,229 @@ { "name": "flood", - "version": "1.0.0", + "version": "4.3.1", + "description": "A modern Web UI for various torrent clients with multi-user and multi-client support", + "keywords": [ + "typescript", + "react", + "node", + "webui", + "web ui", + "rTorrent", + "qBittorrent", + "Transmission" + ], "private": false, "license": "GPL-3.0-only", "repository": { "type": "git", - "url": "https://github.com/Flood-UI/flood.git" + "url": "https://github.com/jesec/flood.git" + }, + "bugs": { + "url": "https://github.com/jesec/flood/issues" + }, + "files": [ + "dist" + ], + "bin": { + "flood": "dist/index.js" + }, + "pkg": { + "assets": [ + "dist/assets/**/*", + "dist/geoip-country.dat", + "dist/geoip-country6.dat" + ], + "targets": [ + "node14-linux-x64", + "node14-macos-x64", + "node14-win-x64" + ] }, "scripts": { - "build": "node client/scripts/build.js", - "build-assets": "UPDATED_SCRIPT=build npm run deprecated-warning && npm run build", - "build-docs": "jsdoc -c ./.jsdoc.json", - "deprecated-warning": "node client/scripts/deprecated-warning.js && sleep 10", - "format-source": "node scripts/prettier.js formatSource", - "check-source-formatting": "node scripts/prettier.js check", + "build": "npm run build-assets && npm run build-ts", + "build-assets": "node client/scripts/build.js", + "build-ts": "ncc build server/bin/start.ts -m -t -e geoip-country", + "build-i18n": "formatjs compile --ast --format simple client/src/javascript/i18n/strings.json --out-file client/src/javascript/i18n/strings.compiled.json && formatjs compile-folder --ast --format simple client/src/javascript/i18n/translations client/src/javascript/i18n/compiled", + "build-pkg": "rm -rf dist && npm run build && pkg . --out-path dist-pkg", + "format-source": "prettier -w .", + "check-compiled-i18n": "npm run build-i18n && npm run format-source && git diff --quiet client/src/javascript/i18n/compiled", + "check-source-formatting": "prettier -c .", "check-types": "tsc", "lint": "NODE_ENV=development eslint --max-warnings 0 . --ext .js --ext .jsx --ext .ts --ext .tsx", - "start": "node --use_strict server/bin/start.js", - "start:development": "UPDATED_SCRIPT=start:development:server npm run deprecated-warning && npm run start:development:server", + "prepack": "rm -rf dist && npm run build", + "start": "node --use_strict dist/index.js", "start:development:client": "node client/scripts/start.js", - "start:development:server": "NODE_ENV=development nodemon server/bin/start.js", - "start:production": "UPDATED_SCRIPT=start npm run deprecated-warning && npm start", - "start:watch": "UPDATED_SCRIPT=start:development:client npm run deprecated-warning && npm run start:development:client", - "test": "node client/scripts/test.js --env=jsdom" + "start:development:server": "NODE_ENV=development TS_NODE_PROJECT=server/tsconfig.json ts-node-dev --transpile-only server/bin/start.ts", + "start:test:rtorrent": "node scripts/testsetup.js", + "test": "jest --forceExit", + "test:watch": "jest --watchAll --forceExit", + "test:client": "FLOOD_OPTION_port=4200 start-server-and-test start 4200 'cypress run'" }, "dependencies": { - "@babel/core": "^7.7.4", - "@babel/preset-env": "^7.7.4", - "@babel/preset-react": "^7.7.4", - "@babel/preset-typescript": "^7.7.4", - "@types/react-dom": "^16.9.4", - "@types/react-intl": "^2.3.18", - "@typescript-eslint/parser": "^1.13.0", - "argon2": "^0.24.1", - "autoprefixer": "^8.6.5", - "axios": "^0.19.0", - "babel-eslint": "^10.0.3", - "babel-loader": "^8.0.6", - "body-parser": "^1.18.3", - "case-sensitive-paths-webpack-plugin": "2.1.2", - "chalk": "^2.3.2", + "geoip-country": "^4.0.46" + }, + "devDependencies": { + "@babel/core": "^7.12.10", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-decorators": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", + "@babel/preset-env": "^7.12.10", + "@babel/preset-react": "^7.12.10", + "@babel/preset-typescript": "^7.12.7", + "@formatjs/cli": "^2.13.15", + "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3", + "@types/bencode": "^2.0.0", + "@types/body-parser": "^1.19.0", + "@types/classnames": "^2.2.11", + "@types/clipboard": "^2.0.1", + "@types/compression": "^1.7.0", + "@types/content-disposition": "^0.5.3", + "@types/cookie-parser": "^1.4.2", + "@types/create-torrent": "^4.4.0", + "@types/d3": "^6.2.0", + "@types/debug": "^4.1.5", + "@types/express": "^4.17.9", + "@types/express-rate-limit": "^5.1.0", + "@types/fs-extra": "^9.0.5", + "@types/geoip-country": "^4.0.0", + "@types/http-errors": "^1.8.0", + "@types/jest": "^26.0.19", + "@types/jsonwebtoken": "^8.5.0", + "@types/morgan": "^1.9.2", + "@types/nedb": "^1.8.11", + "@types/node": "^12.19.9", + "@types/overlayscrollbars": "^1.12.0", + "@types/passport": "^1.0.4", + "@types/passport-jwt": "^3.0.3", + "@types/react": "^17.0.0", + "@types/react-dnd-multi-backend": "^6.0.0", + "@types/react-dom": "^17.0.0", + "@types/react-measure": "^2.0.6", + "@types/react-router-dom": "^5.1.6", + "@types/react-transition-group": "^4.4.0", + "@types/react-window": "^1.8.2", + "@types/spdy": "^3.4.4", + "@types/supertest": "^2.0.10", + "@types/tar": "^4.0.4", + "@typescript-eslint/eslint-plugin": "^4.9.1", + "@typescript-eslint/parser": "^4.9.1", + "@vercel/ncc": "^0.25.1", + "autoprefixer": "^10.1.0", + "axios": "^0.21.0", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.2.2", + "bencode": "^2.0.1", + "body-parser": "^1.19.0", + "case-sensitive-paths-webpack-plugin": "2.3.0", + "chalk": "^4.1.0", "classnames": "^2.2.6", - "clipboard": "^2.0.4", - "compression": "^1.7.3", - "cookie-parser": "^1.4.3", - "css-loader": "^3.2.0", - "d3": "^3.5.17", - "debug": "^3.2.6", - "deep-equal": "^1.1.1", + "clipboard": "^2.0.6", + "compression": "^1.7.4", + "content-disposition": "^0.5.3", + "cookie-parser": "^1.4.5", + "create-torrent": "^4.4.2", + "css-loader": "^5.0.1", + "css-minimizer-webpack-plugin": "^1.1.5", + "d3-array": "^2.9.1", + "d3-scale": "^3.2.3", + "d3-selection": "^2.0.0", + "d3-shape": "^2.0.0", + "debug": "^4.3.1", + "eslint": "^7.15.0", + "eslint-config-airbnb": "^18.2.1", + "eslint-config-airbnb-typescript": "^12.0.0", + "eslint-config-prettier": "^7.0.0", + "eslint-config-react-app": "^6.0.0", + "eslint-import-resolver-webpack": "^0.13.0", + "eslint-plugin-flowtype": "5.2.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jest": "^24.1.3", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-react": "^7.21.5", + "eslint-plugin-react-hooks": "^4.2.0", + "eslint-webpack-plugin": "^2.4.1", "express": "^4.17.1", - "feedsub": "^0.6.0", - "file-loader": "^4.3.0", - "flood-ui-kit": "^0.1.11", - "flux": "^3.1.3", - "fs-extra": "^5.0.0", - "geoip-country": "^3.3.1", - "html-webpack-plugin": "^4.0.0-beta.11", - "joi": "^13.7.0", - "jsonwebtoken": "^8.4.0", - "lodash": "^4.17.15", - "mkdirp": "^0.5.1", - "moment": "^2.22.2", - "morgan": "^1.9.1", - "multer": "^1.4.2", - "mv": "^2.1.1", + "express-rate-limit": "^5.2.3", + "fast-json-patch": "^3.0.0-1", + "fast-sort": "^2.2.0", + "feedsub": "^0.7.6", + "file-loader": "^6.2.0", + "form-data": "^3.0.0", + "frontmatter-markdown-loader": "^3.6.2", + "fs-extra": "^9.0.1", + "get-user-locale": "^1.4.0", + "hash-wasm": "^4.4.1", + "html-webpack-plugin": "^5.0.0-alpha.10", + "http-errors": "^1.8.0", + "jest": "^26.6.3", + "js-file-download": "^0.4.12", + "jsonwebtoken": "^8.5.1", + "lodash": "^4.17.20", + "mini-css-extract-plugin": "^1.3.3", + "mobx": "^6.0.4", + "mobx-react": "^7.0.5", + "morgan": "^1.10.0", "nedb": "^1.8.0", - "node-sass": "^4.13.0", - "object-assign": "4.1.1", - "ospath": "^1.2.2", - "pascal-case": "^2.0.1", - "passport": "^0.4.0", + "nedb-promises": "^4.1.0", + "node-sass": "^5.0.0", + "overlayscrollbars": "^1.13.0", + "overlayscrollbars-react": "^0.2.2", + "passport": "^0.4.1", "passport-jwt": "^4.0.0", - "postcss-loader": "2.1.3", - "promise": "^8.0.2", - "pug": "^2.0.4", - "react": "^16.12.0", - "react-custom-scrollbars": "^4.2.1", - "react-dnd": "^2.6.0", - "react-dnd-html5-backend": "^2.6.0", - "react-dom": "^16.12.0", - "react-dropzone": "^4.3.0", - "react-intl": "^2.9.0", - "react-markdown": "^4.2.2", - "react-router": "^5.1.2", - "react-router-dom": "^5.1.2", - "react-transition-group": "^4.3.0", - "ress": "^1.2.2", - "rimraf": "^2.7.1", - "run-series": "^1.1.6", - "sass-loader": "^7.3.1", - "saxen": "8.1.x", - "source-map-loader": "^0.2.4", - "spdy": "^3.4.7", - "style-loader": "0.20.3", - "svg-sprite-loader": "^4.1.6", - "tar-stream": "^1.6.2", - "terser-webpack-plugin": "^1.4.1", - "typed-css-modules": "^0.5.1", - "url-loader": "^2.3.0", - "xmlrpc": "^1.3.2" - }, - "devDependencies": { - "@babel/plugin-proposal-class-properties": "^7.7.4", - "@types/classnames": "^2.2.9", - "@types/keymirror": "^0.1.1", - "@types/node": "^11.15.2", - "@types/react": "^16.9.13", - "@types/react-router-dom": "^4.3.5", - "@types/react-transition-group": "^2.0.16", - "@typescript-eslint/eslint-plugin": "^1.13.0", - "babel-jest": "22.4.3", - "eslint": "^5.8.0", - "eslint-config-airbnb": "^17.1.1", - "eslint-config-prettier": "^4.3.0", - "eslint-config-react-app": "^2.1.0", - "eslint-import-resolver-webpack": "^0.11.1", - "eslint-loader": "^2.2.1", - "eslint-plugin-flowtype": "2.46.1", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-react": "^7.16.0", - "glob": "^7.1.6", - "jest": "22.4.3", - "jsdoc": "^3.6.3", - "minami": "^1.2.3", - "nodemon": "^1.19.4", - "prettier": "1.14.2", - "react-dev-utils": "^9.1.0", - "typescript": "^3.7.2", - "webpack": "^4.41.2", - "webpack-cli": "^3.3.10", - "webpack-dev-server": "^3.9.0", - "webpack-manifest-plugin": "^2.2.0" - }, - "eslintConfig": { - "extends": "react-app" + "postcss": "^8.2.1", + "postcss-loader": "^4.1.0", + "prettier": "^2.2.1", + "promise": "^8.1.0", + "query-string": "^6.13.7", + "react": "^17.0.1", + "react-dev-utils": "^11.0.1", + "react-dnd": "^11.1.3", + "react-dnd-html5-backend": "^11.1.3", + "react-dnd-multi-backend": "^6.0.2", + "react-dnd-touch-backend": "^11.1.3", + "react-dom": "^17.0.1", + "react-dropzone": "^11.2.4", + "react-intl": "^5.10.6", + "react-measure": "^2.5.2", + "react-refresh": "^0.9.0", + "react-router": "^5.2.0", + "react-router-dom": "^5.2.0", + "react-transition-group": "^4.4.1", + "react-use": "^15.3.4", + "react-window": "^1.8.6", + "ress": "^3.0.0", + "sanitize-filename": "^1.6.3", + "sass-loader": "^10.1.0", + "saxen": "^8.1.2", + "source-map-loader": "^1.1.3", + "spdy": "^4.0.2", + "style-loader": "^2.0.0", + "supertest": "^6.0.1", + "tar": "^6.0.5", + "terser-webpack-plugin": "^5.0.3", + "ts-jest": "^26.4.4", + "ts-node-dev": "^1.1.1", + "typed-emitter": "^1.3.1", + "typescript": "^4.1.3", + "url-loader": "^4.1.1", + "use-query-params": "^1.1.9", + "webpack": "^5.10.1", + "webpack-dev-server": "^4.0.0-beta.0", + "webpackbar": "^5.0.0-3", + "xmlrpc": "^1.3.2", + "yargs": "^16.2.0", + "zod": "^1.11.11" }, "engines": { - "node": ">=7.0.0", - "npm": "~5.3.0" - } + "node": ">=12.0.0", + "npm": ">=6.0.0" + }, + "browserslist": [ + "> 2%", + "last 10 chrome version", + "last 5 firefox version", + "last 5 edge version", + "last 2 iOS version", + "last 2 Safari version" + ] } diff --git a/scripts/prettier.js b/scripts/prettier.js deleted file mode 100644 index 965077b3b..000000000 --- a/scripts/prettier.js +++ /dev/null @@ -1,108 +0,0 @@ -const chalk = require('chalk'); -const fs = require('fs'); -const glob = require('glob'); -const path = require('path'); -const prettier = require('prettier'); - -const SOURCE_PATTERN = `{client,scripts,server,shared}${path.sep}!(assets){${path.sep},}{**${ - path.sep -}*,*}.{js,jsx,ts,tsx,json,md}`; - -const readFile = filePath => { - return new Promise((resolve, reject) => { - fs.readFile(filePath, 'utf8', (error, fileContent) => { - if (error != null) { - reject(error); - return; - } - - resolve(fileContent); - }); - }); -}; - -const writeFile = (filePath, fileContent) => { - return new Promise((resolve, reject) => { - fs.writeFile(filePath, fileContent, writeFileError => { - if (writeFileError) { - reject(writeFileError); - return; - } - - resolve(filePath); - }); - }); -}; - -const formatFile = async (inputFilePath, outputFilePath) => { - const fileContent = await readFile(inputFilePath); - const prettierConfig = await prettier.resolveConfig(inputFilePath); - const writtenFilePath = await writeFile( - outputFilePath, - prettier.format(fileContent, {...prettierConfig, filepath: inputFilePath}), - ); - - return writtenFilePath; -}; - -const getSourceFilePaths = () => { - return new Promise((resolve, reject) => { - glob(SOURCE_PATTERN, (error, files) => { - if (error) { - reject(error); - return; - } - - resolve(files.map(filePath => path.join(process.cwd(), filePath))); - }); - }); -}; - -const formatSource = async () => { - console.log(chalk.reset('Formatting source files...')); - - try { - const sourceFilePaths = await getSourceFilePaths(); - const formattedPaths = await Promise.all(sourceFilePaths.map(filePath => formatFile(filePath, filePath))); - - console.log(chalk.green(`Formatted ${formattedPaths.length} files.`)); - } catch (error) { - console.log(chalk.red('Problem formatting file:\n'), chalk.reset(error)); - process.exit(1); - } -}; - -const check = async () => { - console.log(chalk.reset('Validating source file formatting...')); - - try { - const sourceFilePaths = await getSourceFilePaths(); - await Promise.all( - sourceFilePaths.map(async filePath => { - const fileContent = await readFile(filePath); - const prettierConfig = await prettier.resolveConfig(filePath); - const isCompliant = prettier.check(fileContent, {...prettierConfig, filepath: filePath}); - - if (!isCompliant) { - throw filePath; - } - }), - ); - - console.log(chalk.green('Finished validating file formatting.')); - } catch (error) { - console.log(chalk.red('Unformatted file found:\n'), chalk.reset(error)); - process.exit(1); - } -}; - -const commands = {check, formatSource}; -const desiredCommand = process.argv.slice(2)[0]; - -if (commands[desiredCommand] != null) { - commands[desiredCommand](); -} - -module.exports = { - formatFile, -}; diff --git a/scripts/testsetup.js b/scripts/testsetup.js new file mode 100644 index 000000000..ccfad7b22 --- /dev/null +++ b/scripts/testsetup.js @@ -0,0 +1,71 @@ +const chalk = require('chalk'); +const crypto = require('crypto'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const {spawn} = require('child_process'); + +const temporaryRuntimeDirectory = path.resolve(os.tmpdir(), `flood.test.${crypto.randomBytes(12).toString('hex')}`); + +console.log(chalk.cyan(`Temporary runtime directory: ${temporaryRuntimeDirectory}\n`)); + +const rTorrentSession = path.join(temporaryRuntimeDirectory, '.session'); +const rTorrentSocket = path.join(temporaryRuntimeDirectory, 'rtorrent.sock'); + +fs.mkdirSync(rTorrentSession, {recursive: true}); + +fs.writeFileSync( + `${temporaryRuntimeDirectory}/rtorrent.rc`, + ` +execute.nothrow = rm,-rf,${rTorrentSession}/rtorrent.lock +directory.default.set = "${temporaryRuntimeDirectory}" +session.path.set = "${rTorrentSession}" +network.scgi.open_local = "${rTorrentSocket}" +`, +); + +let argv = []; +argv.push('--rundir', temporaryRuntimeDirectory); +argv.push('--auth', 'none'); +argv.push('--rtsocket', rTorrentSocket); +argv.push('--allowedpath', temporaryRuntimeDirectory); +argv.push('--rtorrent'); +argv.push('--rtconfig', `${temporaryRuntimeDirectory}/rtorrent.rc`); +argv.push('--test'); +argv = argv.concat(process.argv.slice(2)); + +let floodProcess; + +const startFlood = () => { + if (floodProcess != null) { + return; + } + + floodProcess = spawn('npm', ['run', 'start:development:server', '--'].concat(argv), { + cwd: path.join(__dirname, '..'), + stdio: 'inherit', + }); +}; + +const closeProcesses = () => { + floodProcess.on('close', () => { + if (process.env.CI !== 'true') { + // TODO: This leads to test flakiness caused by ENOENT error + // NeDB provides no method to close database connection + fs.rmdirSync(temporaryRuntimeDirectory, {recursive: true}); + } + }); + + floodProcess.kill('SIGTERM'); + process.kill(Number(fs.readFileSync(`${temporaryRuntimeDirectory}/rtorrent.pid`).toString())); +}; + +startFlood(); + +process.on('SIGINT', () => { + process.exit(); +}); + +process.on('exit', () => { + closeProcesses(); +}); diff --git a/server/.eslintrc.js b/server/.eslintrc.js deleted file mode 100644 index 331daec2a..000000000 --- a/server/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - env: { - browser: 0, - node: 1, - }, -}; diff --git a/server/.eslintrc.json b/server/.eslintrc.json new file mode 100644 index 000000000..cb1e7d118 --- /dev/null +++ b/server/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": "../.eslintrc.json", + + "rules": { + "no-restricted-imports": [ + "error", + { + "patterns": ["**/client/**/*"] + } + ], + "no-restricted-modules": [ + "error", + { + "patterns": ["**/client/**/*"] + } + ], + // TODO: Explicit return type + "@typescript-eslint/explicit-function-return-type": 0, + "@typescript-eslint/explicit-module-boundary-types": 0 + } +} diff --git a/server/.jest/auth.config.js b/server/.jest/auth.config.js new file mode 100644 index 000000000..f04b8e047 --- /dev/null +++ b/server/.jest/auth.config.js @@ -0,0 +1,13 @@ +module.exports = { + displayName: 'auth', + preset: 'ts-jest/presets/js-with-babel', + rootDir: './../', + testEnvironment: 'node', + testMatch: ['/routes/api/auth.test.ts'], + setupFilesAfterEnv: ['/.jest/auth.setup.js'], + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, +}; diff --git a/server/.jest/auth.setup.js b/server/.jest/auth.setup.js new file mode 100644 index 000000000..9fbc256e6 --- /dev/null +++ b/server/.jest/auth.setup.js @@ -0,0 +1,19 @@ +import crypto from 'crypto'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; + +const temporaryRuntimeDirectory = path.resolve(os.tmpdir(), `flood.test.${crypto.randomBytes(12).toString('hex')}`); + +process.argv = ['node', 'flood']; +process.argv.push('--rundir', temporaryRuntimeDirectory); +process.argv.push('--auth', 'default'); +process.argv.push('--assets', 'false'); + +afterAll(() => { + if (process.env.CI !== 'true') { + // TODO: This leads to test flakiness caused by ENOENT error + // NeDB provides no method to close database connection + fs.rmdirSync(temporaryRuntimeDirectory, {recursive: true}); + } +}); diff --git a/server/.jest/qbittorrent.config.js b/server/.jest/qbittorrent.config.js new file mode 100644 index 000000000..27acec67b --- /dev/null +++ b/server/.jest/qbittorrent.config.js @@ -0,0 +1,13 @@ +module.exports = { + displayName: 'qbittorrent', + preset: 'ts-jest/presets/js-with-babel', + rootDir: './../', + testEnvironment: 'node', + testPathIgnorePatterns: ['auth.test.ts'], + setupFilesAfterEnv: ['/.jest/qbittorrent.setup.js'], + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, +}; diff --git a/server/.jest/qbittorrent.setup.js b/server/.jest/qbittorrent.setup.js new file mode 100644 index 000000000..cace017fc --- /dev/null +++ b/server/.jest/qbittorrent.setup.js @@ -0,0 +1,34 @@ +import crypto from 'crypto'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import {spawn} from 'child_process'; + +const temporaryRuntimeDirectory = path.resolve(os.tmpdir(), `flood.test.${crypto.randomBytes(12).toString('hex')}`); + +fs.mkdirSync(temporaryRuntimeDirectory, {recursive: true}); + +const qbtPort = Math.floor(Math.random() * (65534 - 20000) + 20000); + +const qBittorrentDaemon = spawn( + 'qbittorrent-nox', + [`--webui-port=${qbtPort}`, `--profile=${temporaryRuntimeDirectory}`], + { + stdio: 'ignore', + killSignal: 'SIGKILL', + }, +); + +process.argv = ['node', 'flood']; +process.argv.push('--rundir', temporaryRuntimeDirectory); +process.argv.push('--allowedpath', temporaryRuntimeDirectory); +process.argv.push('--auth', 'none'); +process.argv.push('--qburl', `http://127.0.0.1:${qbtPort}`); +process.argv.push('--qbuser', 'admin'); +process.argv.push('--qbpass', 'adminadmin'); +process.argv.push('--assets', 'false'); + +afterAll(() => { + qBittorrentDaemon.kill('SIGKILL'); + fs.rmdirSync(temporaryRuntimeDirectory, {recursive: true}); +}); diff --git a/server/.jest/rtorrent.config.js b/server/.jest/rtorrent.config.js new file mode 100644 index 000000000..502d27628 --- /dev/null +++ b/server/.jest/rtorrent.config.js @@ -0,0 +1,13 @@ +module.exports = { + displayName: 'rtorrent', + preset: 'ts-jest/presets/js-with-babel', + rootDir: './../', + testEnvironment: 'node', + testPathIgnorePatterns: ['auth.test.ts'], + setupFilesAfterEnv: ['/.jest/rtorrent.setup.js'], + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, +}; diff --git a/server/.jest/rtorrent.setup.js b/server/.jest/rtorrent.setup.js new file mode 100644 index 000000000..16de9c0c4 --- /dev/null +++ b/server/.jest/rtorrent.setup.js @@ -0,0 +1,40 @@ +import crypto from 'crypto'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; + +const temporaryRuntimeDirectory = path.resolve(os.tmpdir(), `flood.test.${crypto.randomBytes(12).toString('hex')}`); + +const rTorrentSession = path.join(temporaryRuntimeDirectory, '.session'); +const rTorrentSocket = path.join(temporaryRuntimeDirectory, 'rtorrent.sock'); + +fs.mkdirSync(rTorrentSession, {recursive: true}); + +fs.writeFileSync( + `${temporaryRuntimeDirectory}/rtorrent.rc`, + ` +directory.default.set = "${temporaryRuntimeDirectory}" +session.path.set = "${rTorrentSession}" +network.scgi.open_local = "${rTorrentSocket}" +`, +); + +process.argv = ['node', 'flood']; +process.argv.push('--rundir', temporaryRuntimeDirectory); +process.argv.push('--auth', 'none'); +process.argv.push('--rtsocket', rTorrentSocket); +process.argv.push('--allowedpath', temporaryRuntimeDirectory); +process.argv.push('--rtorrent'); +process.argv.push('--rtconfig', `${temporaryRuntimeDirectory}/rtorrent.rc`); +process.argv.push('--test'); +process.argv.push('--assets', 'false'); + +afterAll((done) => { + process.kill(Number(fs.readFileSync(`${temporaryRuntimeDirectory}/rtorrent.pid`).toString())); + if (process.env.CI !== 'true') { + // TODO: This leads to test flakiness caused by ENOENT error + // NeDB provides no method to close database connection + fs.rmdirSync(temporaryRuntimeDirectory, {recursive: true}); + } + done(); +}); diff --git a/server/.jest/transmission.config.js b/server/.jest/transmission.config.js new file mode 100644 index 000000000..66f7ce357 --- /dev/null +++ b/server/.jest/transmission.config.js @@ -0,0 +1,13 @@ +module.exports = { + displayName: 'transmission', + preset: 'ts-jest/presets/js-with-babel', + rootDir: './../', + testEnvironment: 'node', + testPathIgnorePatterns: ['auth.test.ts'], + setupFilesAfterEnv: ['/.jest/transmission.setup.js'], + globals: { + 'ts-jest': { + isolatedModules: true, + }, + }, +}; diff --git a/server/.jest/transmission.setup.js b/server/.jest/transmission.setup.js new file mode 100644 index 000000000..cbcd9b8c6 --- /dev/null +++ b/server/.jest/transmission.setup.js @@ -0,0 +1,55 @@ +import crypto from 'crypto'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import {spawn} from 'child_process'; + +const temporaryRuntimeDirectory = path.resolve(os.tmpdir(), `flood.test.${crypto.randomBytes(12).toString('hex')}`); + +const transmissionSession = path.join(temporaryRuntimeDirectory, 'transmission'); +const rpcPort = Math.floor(Math.random() * (65534 - 20000) + 20000); + +fs.mkdirSync(transmissionSession, {recursive: true}); + +const transmissionProcess = spawn( + 'transmission-daemon', + [ + '-f', + '-w', + temporaryRuntimeDirectory, + '-g', + transmissionSession, + '-p', + `${rpcPort}`, + '-u', + 'transmission', + '-v', + 'transmission', + ], + { + stdio: 'ignore', + killSignal: 'SIGKILL', + }, +); + +process.argv = ['node', 'flood']; +process.argv.push('--rundir', temporaryRuntimeDirectory); +process.argv.push('--allowedpath', temporaryRuntimeDirectory); +process.argv.push('--auth', 'none'); +process.argv.push('--trurl', `http://127.0.0.1:${rpcPort}/transmission/rpc`); +process.argv.push('--truser', 'transmission'); +process.argv.push('--trpass', 'transmission'); +process.argv.push('--assets', 'false'); + +afterAll((done) => { + transmissionProcess.on('close', () => { + if (process.env.CI !== 'true') { + // TODO: This leads to test flakiness caused by ENOENT error + // NeDB provides no method to close database connection + fs.rmdirSync(temporaryRuntimeDirectory, {recursive: true}); + } + done(); + }); + + transmissionProcess.kill('SIGKILL'); +}); diff --git a/server/app.js b/server/app.js deleted file mode 100644 index b7b755b4f..000000000 --- a/server/app.js +++ /dev/null @@ -1,74 +0,0 @@ -require('events').EventEmitter.defaultMaxListeners = Infinity; - -const bodyParser = require('body-parser'); -const compression = require('compression'); -const cookieParser = require('cookie-parser'); -const express = require('express'); -const morgan = require('morgan'); -const passport = require('passport'); -const path = require('path'); - -const app = express(); -const apiRoutes = require('./routes/api'); -const authRoutes = require('./routes/auth'); -const paths = require('../client/config/paths'); -const Users = require('./models/Users'); - -Users.bootstrapServicesForAllUsers(); - -// Remove Express header -if (process.env.NODE_ENV !== 'development') { - app.disable('x-powered-by'); -} - -app.set('etag', false); -app.set('views', path.join(__dirname, 'views')); -app.set('view engine', 'pug'); - -app.use(morgan('dev')); -app.use(passport.initialize()); -app.use(compression()); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({extended: false})); -app.use(cookieParser()); - -require('./config/passport')(passport); - -app.use('/api', apiRoutes); -app.use('/auth', authRoutes); - -// After routes, look for static assets. -app.use(express.static(paths.appBuild)); -// After static assets, always return index.html -app.use((req, res) => res.sendFile(path.join(paths.appBuild, 'index.html'))); - -// Catch 404 and forward to error handler. -app.use((req, res, next) => { - const err = new Error('Not Found'); - err.status = 404; - next(err); -}); - -// Development error handler, will print stacktrace. -if (app.get('env') === 'development') { - app.use((err, req, res) => { - res.status(err.status || 500); - res.render('error', { - message: err.message, - error: err, - title: 'Flood Error', - }); - }); -} else { - // Production error handler, no stacktraces leaked to user. - app.use((err, req, res) => { - res.status(err.status || 500); - res.render('error', { - message: err.message, - error: {}, - title: 'Flood Error', - }); - }); -} - -module.exports = app; diff --git a/server/app.ts b/server/app.ts new file mode 100644 index 000000000..8674bfb36 --- /dev/null +++ b/server/app.ts @@ -0,0 +1,85 @@ +import bodyParser from 'body-parser'; +import compression from 'compression'; +import cookieParser from 'cookie-parser'; +import express from 'express'; +import fs from 'fs'; +import morgan from 'morgan'; +import passport from 'passport'; +import path from 'path'; + +import type {UserInDatabase} from '@shared/schema/Auth'; + +import apiRoutes from './routes/api'; +import config from '../config'; +import passportConfig from './config/passport'; +import paths from '../shared/config/paths'; +import Users from './models/Users'; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Express { + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface User extends UserInDatabase {} + } +} + +const app = express(); + +Users.bootstrapServicesForAllUsers(); + +// Remove Express header +if (process.env.NODE_ENV !== 'development') { + app.disable('x-powered-by'); +} + +app.set('strict routing', true); +app.set('trust proxy', 'loopback'); + +app.use(morgan('dev')); + +if (config.serveAssets !== false) { + // Disable ETag + app.set('etag', false); + + // Enable compression + app.use(compression()); + + // Static assets + app.use(paths.servedPath, express.static(paths.appDist)); + + // Client app routes, serve index.html and client js will figure it out + const html = fs.readFileSync(path.join(paths.appDist, 'index.html'), { + encoding: 'utf8', + }); + + app.get(`${paths.servedPath}login`, (_req, res) => { + res.send(html); + }); + + app.get(`${paths.servedPath}register`, (_req, res) => { + res.send(html); + }); + + app.get(`${paths.servedPath}overview`, (_req, res) => { + res.send(html); + }); +} else { + // no-op res.flush() as compression is not handled by Express + app.use((_req, res, next) => { + res.flush = () => { + // do nothing. + }; + next(); + }); +} + +app.use(passport.initialize()); +app.use(bodyParser.json({limit: '50mb'})); +app.use(bodyParser.urlencoded({extended: false, limit: '50mb'})); +app.use(cookieParser()); + +passportConfig(passport); + +app.use(`${paths.servedPath}api`, apiRoutes); + +export default app; diff --git a/server/bin/enforce-prerequisites.js b/server/bin/enforce-prerequisites.js deleted file mode 100644 index beb20060a..000000000 --- a/server/bin/enforce-prerequisites.js +++ /dev/null @@ -1,39 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const staticAssets = [path.join(__dirname, '../assets/index.html')]; - -const configFiles = [path.join(__dirname, '../../config.js')]; - -// Taken from react-scripts/check-required-files, but without console.logs. -const doFilesExist = files => { - try { - files.forEach(filename => { - fs.accessSync(filename, fs.F_OK); - }); - return true; - } catch (err) { - return false; - } -}; - -const enforcePrerequisites = () => - new Promise((resolve, reject) => { - if (!doFilesExist(configFiles)) { - reject(new Error(`Configuration files missing. Please check the 'Configuring' section of README.md.`)); - return; - } - - if (!doFilesExist(staticAssets)) { - reject( - new Error( - `Static assets (index.html) are missing. Please check the 'Compiling assets and starting the server' section of README.md.`, - ), - ); - return; - } - - return resolve(); - }); - -module.exports = enforcePrerequisites; diff --git a/server/bin/enforce-prerequisites.ts b/server/bin/enforce-prerequisites.ts new file mode 100644 index 000000000..0d892ff71 --- /dev/null +++ b/server/bin/enforce-prerequisites.ts @@ -0,0 +1,47 @@ +import fs from 'fs'; +import path from 'path'; + +import {appDist} from '../../shared/config/paths'; +import config from '../../config'; +import {configSchema} from '../../shared/schema/Config'; + +const staticAssets = [path.join(appDist, 'index.html')]; + +// Taken from react-scripts/check-required-files, but without console.logs. +const doFilesExist = (files: Array) => { + try { + files.forEach((filename) => { + fs.accessSync(filename, fs.constants.F_OK); + }); + return true; + } catch (err) { + return false; + } +}; + +const enforcePrerequisites = () => + new Promise((resolve, reject: (error: Error) => void) => { + // Ensures that WebAssembly support is present + if (typeof WebAssembly === 'undefined') { + reject(new Error('WebAssembly is not supported in this environment!')); + return; + } + + // Ensures that there is a proper configuration + const result = configSchema.safeParse(config); + if (!result.success) { + console.error(result.error.message); + reject(new Error('Invalid configuration.')); + return; + } + + // Ensure static assets exist if they need to be served + if (!doFilesExist(staticAssets) && config.serveAssets !== false) { + reject(new Error(`Static assets are missing.`)); + return; + } + + resolve(); + }); + +export default enforcePrerequisites; diff --git a/server/bin/migrations/UserInDatabase2.ts b/server/bin/migrations/UserInDatabase2.ts new file mode 100644 index 000000000..8f7c69fa7 --- /dev/null +++ b/server/bin/migrations/UserInDatabase2.ts @@ -0,0 +1,65 @@ +import {AccessLevel} from '../../../shared/schema/constants/Auth'; +import Users from '../../models/Users'; + +import type {Credentials} from '../../../shared/schema/Auth'; +import type {RTorrentConnectionSettings} from '../../../shared/schema/ClientConnectionSettings'; +import type {UserInDatabase1} from './types/UserInDatabase1'; + +const migrationError = (err?: Error) => { + if (err) { + console.error(err); + } + console.error('Migration failed. You need to reset the databases manually.'); + process.exit(); +}; + +const migration = () => { + return Users.listUsers().then((users) => { + return Promise.all( + users.map(async (user) => { + if (user.client != null) { + // No need to migrate. + return; + } + + const userV1 = (user as unknown) as UserInDatabase1; + + let connectionSettings: RTorrentConnectionSettings | null = null; + if (userV1.socketPath != null) { + connectionSettings = { + client: 'rTorrent', + type: 'socket', + version: 1, + socket: userV1.socketPath, + }; + } else if (userV1.host != null && userV1.port != null) { + connectionSettings = { + client: 'rTorrent', + type: 'tcp', + version: 1, + host: userV1.host, + port: userV1.port, + }; + } + + if (connectionSettings == null) { + throw new Error('Corrupted client connection settings.'); + } + + const userV2: Credentials = { + username: userV1.username, + password: userV1.password, + client: connectionSettings, + level: userV1.isAdmin ? AccessLevel.ADMINISTRATOR : AccessLevel.USER, + }; + + await Users.removeUser(userV1.username); + await Users.createUser(userV2, false); + }), + ).catch((err) => { + migrationError(err); + }); + }); +}; + +export default migration; diff --git a/server/bin/migrations/fix-is-admin-flag.js b/server/bin/migrations/fix-is-admin-flag.js deleted file mode 100644 index 1fd9e617f..000000000 --- a/server/bin/migrations/fix-is-admin-flag.js +++ /dev/null @@ -1,49 +0,0 @@ -const chalk = require('chalk'); -const Users = require('../../models/Users'); - -const log = data => { - if (process.env.DEBUG) { - console.log(data); - } -}; - -const migrate = () => { - log(chalk.green('Migrating data: resolving unset isAdmin flag')); - - return new Promise((migrateResolve, migrateReject) => { - Users.listUsers((users, migrateError) => { - if (migrateError) return migrateReject(migrateError); - - migrateResolve( - Promise.all( - users.map(user => { - let userPatch = null; - - if (user.isAdmin == null) { - userPatch = {isAdmin: true}; - } - - if (userPatch != null) { - log(chalk.yellow(`Migrating user ${user.username}`)); - - return new Promise((updateUserResolve, updateUserReject) => { - Users.updateUser(user.username, userPatch, (response, updateUserError) => { - if (updateUserError) { - updateUserReject(updateUserError); - return; - } - - updateUserResolve(response); - }); - }); - } - - return Promise.resolve(); - }), - ), - ); - }); - }); -}; - -module.exports = migrate; diff --git a/server/bin/migrations/per-user-rtorrent-instances.js b/server/bin/migrations/per-user-rtorrent-instances.js deleted file mode 100644 index b9566d5f2..000000000 --- a/server/bin/migrations/per-user-rtorrent-instances.js +++ /dev/null @@ -1,76 +0,0 @@ -const chalk = require('chalk'); -const config = require('../../../config'); -const Users = require('../../models/Users'); - -const log = data => { - if (process.env.DEBUG) { - console.log(data); - } -}; - -const migrate = () => { - log(chalk.green('Migrating data: moving rTorrent connection information to users database')); - - return new Promise((migrateResolve, migrateReject) => { - Users.listUsers((users, listUsersError) => { - if (listUsersError) return migrateReject(listUsersError); - const {scgi = {}} = config; - const existingConfig = { - host: scgi.host, - port: scgi.port, - socketPath: scgi.socketPath, - }; - - migrateResolve( - Promise.all( - users.map(user => { - let userPatch = null; - - // A bug in this script caused all of these xmlrpc values to be defined in the user db. - // If they are all defined, set all to null to prompt the user to provide new connection - // details. - if (user.host != null && user.port != null && user.socketPath != null) { - userPatch = { - host: null, - port: null, - socketPath: null, - }; - } - - // If none of the xmlrpc fields are on the user object, try to infer this from the legacy - // configuration file. - if (user.host == null && user.port == null && user.socketPath == null) { - userPatch = {isAdmin: true}; - - if (existingConfig.socketPath && existingConfig.socketPath.trim().length > 0) { - userPatch.socketPath = existingConfig.socketPath; - } else { - userPatch.host = existingConfig.host; - userPatch.port = existingConfig.port; - } - } - - if (userPatch != null) { - log(chalk.yellow(`Migrating user ${user.username}`)); - - return new Promise((updateUserResolve, updateUserReject) => { - Users.updateUser(user.username, userPatch, (response, updateUserError) => { - if (updateUserError) { - updateUserReject(updateUserError); - return; - } - - updateUserResolve(response); - }); - }); - } - - return Promise.resolve(); - }), - ), - ); - }); - }); -}; - -module.exports = migrate; diff --git a/server/bin/migrations/run.js b/server/bin/migrations/run.js deleted file mode 100644 index 9d3b335c2..000000000 --- a/server/bin/migrations/run.js +++ /dev/null @@ -1,8 +0,0 @@ -const perUserRtorrentInstances = require('./per-user-rtorrent-instances'); -const fixIsAdminFlag = require('./fix-is-admin-flag'); - -const migrations = [perUserRtorrentInstances, fixIsAdminFlag]; - -const migrate = () => Promise.all(migrations.map(migration => migration())); - -module.exports = migrate; diff --git a/server/bin/migrations/run.ts b/server/bin/migrations/run.ts new file mode 100644 index 000000000..81921e8b0 --- /dev/null +++ b/server/bin/migrations/run.ts @@ -0,0 +1,7 @@ +import UserInDatabase2 from './UserInDatabase2'; + +const migrations = [UserInDatabase2]; + +const migrate = () => Promise.all(migrations.map((migration) => migration())); + +export default migrate; diff --git a/server/bin/migrations/types/UserInDatabase1.ts b/server/bin/migrations/types/UserInDatabase1.ts new file mode 100644 index 000000000..9dbd6bfd5 --- /dev/null +++ b/server/bin/migrations/types/UserInDatabase1.ts @@ -0,0 +1,10 @@ +// Deprecated data structure. Not used outside of migration. +export type UserInDatabase1 = { + _id: string; + username: string; + password: string; + host?: string | null; + port?: number | null; + socketPath?: string | null; + isAdmin: boolean; +}; diff --git a/server/bin/start.js b/server/bin/start.js deleted file mode 100644 index d110cc2c0..000000000 --- a/server/bin/start.js +++ /dev/null @@ -1,14 +0,0 @@ -const chalk = require('chalk'); - -const enforcePrerequisites = require('./enforce-prerequisites'); -const migrateData = require('./migrations/run'); -const {startWebServer} = require('./web-server'); - -enforcePrerequisites() - .then(migrateData) - .then(startWebServer) - .catch(error => { - console.log(chalk.red('Failed to start Flood:')); - console.trace(error); - process.exit(1); - }); diff --git a/server/bin/start.ts b/server/bin/start.ts new file mode 100755 index 000000000..ece7568c4 --- /dev/null +++ b/server/bin/start.ts @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +import chalk from 'chalk'; + +import enforcePrerequisites from './enforce-prerequisites'; +import migrateData from './migrations/run'; + +process.env.NODE_ENV = process.env.NODE_ENV !== 'development' ? 'production' : 'development'; + +enforcePrerequisites() + .then(migrateData) + .then(() => { + // We do this because we don't want the side effects of importing server functions before migration is completed. + const startWebServer = require('./web-server').default; // eslint-disable-line @typescript-eslint/no-var-requires + return startWebServer(); + }) + .catch((error) => { + console.log(chalk.red('Failed to start Flood:')); + console.trace(error); + process.exit(1); + }); diff --git a/server/bin/web-server.js b/server/bin/web-server.js deleted file mode 100755 index c0ce9852d..000000000 --- a/server/bin/web-server.js +++ /dev/null @@ -1,94 +0,0 @@ -const chalk = require('chalk'); -const debug = require('debug')('flood:server'); -const fs = require('fs'); -const http = require('http'); -const spdy = require('spdy'); - -const app = require('../app'); -const config = require('../../config'); - -// Normalize a port into a number, string, or false. -const normalizePort = val => { - const port = parseInt(val, 10); - - // Named pipe. - if (Number.isNaN(port)) { - return val; - } - - // Port number. - if (port >= 0) { - return port; - } - - return false; -}; - -const startWebServer = () => { - const port = normalizePort(config.floodServerPort); - const host = config.floodServerHost; - const useSSL = config.ssl; - - app.set('port', port); - app.set('host', host); - - // Create HTTP or HTTPS server. - let server; - - if (useSSL) { - if (!config.sslKey || !config.sslCert) { - console.error('Cannot start HTTPS server, `sslKey` or `sslCert` is missing in config.js.'); - process.exit(1); - } - - server = spdy.createServer( - { - key: fs.readFileSync(config.sslKey), - cert: fs.readFileSync(config.sslCert), - }, - app, - ); - } else { - server = http.createServer(app); - } - - const handleError = error => { - if (error.syscall !== 'listen') { - throw error; - } - - const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`; - - // Handle specific listen errors with friendly messages. - switch (error.code) { - case 'EACCES': - console.error(`${bind} requires elevated privileges`); - process.exit(1); - break; - case 'EADDRINUSE': - console.error(`${bind} is already in use`); - process.exit(1); - break; - default: - throw error; - } - }; - - // Event listener for HTTP server "listening" event. - const handleListening = () => { - const addr = server.address(); - const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`; - debug(`Listening on ${bind}`); - }; - - // Listen on provided port, on all network interfaces. - server.listen(port, host); - server.on('error', handleError); - server.on('listening', handleListening); - - const address = chalk.underline(`${useSSL ? 'https' : 'http'}://${host}:${port}`); - - console.log(chalk.green(`Flood server starting on ${address}.\n`)); -}; - -module.exports = {startWebServer}; diff --git a/server/bin/web-server.ts b/server/bin/web-server.ts new file mode 100755 index 000000000..a9a0512e4 --- /dev/null +++ b/server/bin/web-server.ts @@ -0,0 +1,113 @@ +import chalk from 'chalk'; +import debug from 'debug'; +import fs from 'fs'; +import http from 'http'; +import spdy from 'spdy'; + +import app from '../app'; +import config from '../../config'; +import packageJSON from '../../package.json'; + +const debugFloodServer = debug('flood:server'); + +// Normalize a port into a number, string, or false. +const normalizePort = (val: string | number): string | number => { + const port = parseInt(val as string, 10); + + // Named pipe. + if (Number.isNaN(port)) { + return val; + } + + // Port number. + if (port >= 0) { + return port; + } + + console.error('Unexpected port or pipe'); + process.exit(1); +}; + +const startWebServer = () => { + const port = normalizePort(config.floodServerPort); + const host = config.floodServerHost; + const useSSL = config.ssl ?? false; + + app.set('port', port); + app.set('host', host); + + // Create HTTP or HTTPS server. + let server: http.Server | spdy.Server; + + if (useSSL) { + if (!config.sslKey || !config.sslCert) { + console.error('Cannot start HTTPS server, `sslKey` or `sslCert` is missing in config.js.'); + process.exit(1); + } + + server = spdy.createServer( + { + key: fs.readFileSync(config.sslKey), + cert: fs.readFileSync(config.sslCert), + }, + app, + ); + } else { + server = http.createServer(app); + } + + const handleError = (error: NodeJS.ErrnoException) => { + if (error.syscall !== 'listen') { + throw error; + } + + const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`; + + // Handle specific listen errors with friendly messages. + switch (error.code) { + case 'EACCES': + console.error(`${bind} requires elevated privileges`); + process.exit(1); + case 'EADDRINUSE': + console.error(`${bind} is already in use`); + process.exit(1); + default: + throw error; + } + }; + + // Event listener for HTTP server "listening" event. + const handleListening = () => { + const addr = server.address(); + if (addr == null) { + console.error('Unable to get listening address.'); + process.exit(1); + } + const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`; + debugFloodServer(`Listening on ${bind}`); + }; + + // Listen on provided port, on all network interfaces. + if (typeof port === 'string') { + server.listen(port); + } else { + server.listen(port, host); + } + + server.on('error', handleError); + server.on('listening', handleListening); + + const address = chalk.underline(`${useSSL ? 'https' : 'http'}://${host}:${port}`); + + console.log(chalk.green(`Flood server ${packageJSON.version} starting on ${address}\n`)); + + if (config.authMethod === 'none') { + console.log(chalk.yellow('Starting without builtin authentication\n')); + } + + if (config.serveAssets === false) { + console.log(chalk.blue('Static assets not served\n')); + } +}; + +export default startWebServer; diff --git a/server/config/passport.js b/server/config/passport.js deleted file mode 100644 index 039a41536..000000000 --- a/server/config/passport.js +++ /dev/null @@ -1,36 +0,0 @@ -const JwtStrategy = require('passport-jwt').Strategy; - -const config = require('../../config'); -const Users = require('../models/Users'); - -// Setup work and export for the JWT passport strategy. -module.exports = passport => { - const options = { - jwtFromRequest: req => { - let token = null; - - if (req && req.cookies) { - token = req.cookies.jwt; - } - - return token; - }, - secretOrKey: config.secret, - }; - - passport.use( - new JwtStrategy(options, (jwtPayload, callback) => { - Users.lookupUser({username: jwtPayload.username}, (err, user) => { - if (err) { - return callback(err, false); - } - - if (user) { - return callback(null, user); - } - - return callback(null, false); - }); - }), - ); -}; diff --git a/server/config/passport.ts b/server/config/passport.ts new file mode 100644 index 000000000..830f1d030 --- /dev/null +++ b/server/config/passport.ts @@ -0,0 +1,36 @@ +import {Strategy} from 'passport-jwt'; + +import type {PassportStatic} from 'passport'; +import type {Request} from 'express'; + +import config from '../../config'; +import Users from '../models/Users'; + +// Setup work and export for the JWT passport strategy. +export default (passport: PassportStatic) => { + const options = { + jwtFromRequest: (req: Request) => { + let token = null; + + if (req && req.cookies) { + token = req.cookies.jwt; + } + + return token; + }, + secretOrKey: config.secret, + }; + + passport.use( + new Strategy(options, (jwtPayload, callback) => { + Users.lookupUser(jwtPayload.username).then( + (user) => { + callback(null, user); + }, + (err) => { + callback(err, false); + }, + ); + }), + ); +}; diff --git a/server/constants/clientGatewayServiceEvents.js b/server/constants/clientGatewayServiceEvents.js deleted file mode 100644 index e76b01261..000000000 --- a/server/constants/clientGatewayServiceEvents.js +++ /dev/null @@ -1,12 +0,0 @@ -const objectUtil = require('../../shared/util/objectUtil'); - -const clientGatewayServiceEvents = [ - 'CLIENT_CONNECTION_STATE_CHANGE', - 'PROCESS_TORRENT', - 'PROCESS_TORRENT_LIST_END', - 'PROCESS_TORRENT_LIST_START', - 'PROCESS_TRANSFER_RATE_START', - 'TORRENTS_REMOVED', -]; - -module.exports = objectUtil.createSymbolMapFromArray(clientGatewayServiceEvents); diff --git a/server/constants/diskUsageServiceEvents.js b/server/constants/diskUsageServiceEvents.js deleted file mode 100644 index fe4bad17c..000000000 --- a/server/constants/diskUsageServiceEvents.js +++ /dev/null @@ -1,5 +0,0 @@ -const objectUtil = require('../../shared/util/objectUtil'); - -const diskUsageServiceEvents = ['DISK_USAGE_CHANGE']; - -module.exports = objectUtil.createSymbolMapFromArray(diskUsageServiceEvents); diff --git a/server/constants/fileListPropMap.js b/server/constants/fileListPropMap.js deleted file mode 100644 index da9f9d435..000000000 --- a/server/constants/fileListPropMap.js +++ /dev/null @@ -1,34 +0,0 @@ -const fileListPropMap = new Map(); -const defaultTransformer = value => value; - -fileListPropMap.set('path', { - methodCall: 'f.path=', - transformValue: defaultTransformer, -}); - -fileListPropMap.set('pathComponents', { - methodCall: 'f.path_components=', - transformValue: defaultTransformer, -}); - -fileListPropMap.set('priority', { - methodCall: 'f.priority=', - transformValue: defaultTransformer, -}); - -fileListPropMap.set('sizeBytes', { - methodCall: 'f.size_bytes=', - transformValue: Number, -}); - -fileListPropMap.set('sizeChunks', { - methodCall: 'f.size_chunks=', - transformValue: Number, -}); - -fileListPropMap.set('completedChunks', { - methodCall: 'f.completed_chunks=', - transformValue: Number, -}); - -module.exports = fileListPropMap; diff --git a/server/constants/historyServiceEvents.js b/server/constants/historyServiceEvents.js deleted file mode 100644 index bdbff5770..000000000 --- a/server/constants/historyServiceEvents.js +++ /dev/null @@ -1,17 +0,0 @@ -const historySnapshotTypes = require('../../shared/constants/historySnapshotTypes'); -const objectUtil = require('../../shared/util/objectUtil'); - -const torrentServiceEvents = [ - 'FETCH_TRANSFER_SUMMARY_ERROR', - 'FETCH_TRANSFER_SUMMARY_SUCCESS', - 'TRANSFER_SUMMARY_DIFF_CHANGE', -].concat( - // Create an array of event types based on the available snapshots. - Object.keys(historySnapshotTypes).reduce((accumulator, snapshotType) => { - accumulator.push(`${snapshotType}_SNAPSHOT_FULL_UPDATE`, `${snapshotType}_SNAPSHOT_DIFF_CHANGE`); - - return accumulator; - }, []), -); - -module.exports = objectUtil.createSymbolMapFromArray(torrentServiceEvents); diff --git a/server/constants/notificationServiceEvents.js b/server/constants/notificationServiceEvents.js deleted file mode 100644 index 33d462106..000000000 --- a/server/constants/notificationServiceEvents.js +++ /dev/null @@ -1,5 +0,0 @@ -const objectUtil = require('../../shared/util/objectUtil'); - -const notificationServiceEvents = ['NOTIFICATION_COUNT_CHANGE']; - -module.exports = objectUtil.createSymbolMapFromArray(notificationServiceEvents); diff --git a/server/constants/taxonomyServiceEvents.js b/server/constants/taxonomyServiceEvents.js deleted file mode 100644 index ee5431f05..000000000 --- a/server/constants/taxonomyServiceEvents.js +++ /dev/null @@ -1,5 +0,0 @@ -const objectUtil = require('../../shared/util/objectUtil'); - -const taxonomyServiceEvents = ['TAXONOMY_DIFF_CHANGE']; - -module.exports = objectUtil.createSymbolMapFromArray(taxonomyServiceEvents); diff --git a/server/constants/torrentListPropMap.js b/server/constants/torrentListPropMap.js deleted file mode 100644 index 0ca2d28e5..000000000 --- a/server/constants/torrentListPropMap.js +++ /dev/null @@ -1,246 +0,0 @@ -const regEx = require('../../shared/util/regEx'); - -const torrentListPropMap = new Map(); - -const booleanTransformer = value => value === '1'; -const dateTransformer = dirtyDate => { - if (!dirtyDate) { - return ''; - } - - const date = dirtyDate.trim(); - - if (date === '0') { - return ''; - } - - return date; -}; -const defaultTransformer = value => value; - -torrentListPropMap.set('hash', { - methodCall: 'd.hash=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('name', { - methodCall: 'd.name=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('message', { - methodCall: 'd.message=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('state', { - methodCall: 'd.state=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('isStateChanged', { - methodCall: 'd.state_changed=', - transformValue: booleanTransformer, -}); - -torrentListPropMap.set('isActive', { - methodCall: 'd.is_active=', - transformValue: booleanTransformer, -}); - -torrentListPropMap.set('isComplete', { - methodCall: 'd.complete=', - transformValue: booleanTransformer, -}); - -torrentListPropMap.set('isHashChecking', { - methodCall: 'd.is_hash_checking=', - transformValue: booleanTransformer, -}); - -torrentListPropMap.set('isOpen', { - methodCall: 'd.is_open=', - transformValue: booleanTransformer, -}); - -torrentListPropMap.set('priority', { - methodCall: 'd.priority=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('upRate', { - methodCall: 'd.up.rate=', - transformValue: Number, -}); - -torrentListPropMap.set('upTotal', { - methodCall: 'd.up.total=', - transformValue: Number, -}); - -torrentListPropMap.set('downRate', { - methodCall: 'd.down.rate=', - transformValue: Number, -}); - -torrentListPropMap.set('downTotal', { - methodCall: 'd.down.total=', - transformValue: Number, -}); - -torrentListPropMap.set('ratio', { - methodCall: 'd.ratio=', - transformValue: Number, -}); - -torrentListPropMap.set('bytesDone', { - methodCall: 'd.bytes_done=', - transformValue: Number, -}); - -torrentListPropMap.set('sizeBytes', { - methodCall: 'd.size_bytes=', - transformValue: Number, -}); - -torrentListPropMap.set('peersConnected', { - methodCall: 'd.peers_connected=', - transformValue: Number, -}); - -torrentListPropMap.set('directory', { - methodCall: 'd.directory=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('basePath', { - methodCall: 'd.base_path=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('baseFilename', { - methodCall: 'd.base_filename=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('baseDirectory', { - methodCall: 'd.directory_base=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('seedingTime', { - methodCall: 'd.custom=seedingtime', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('dateAdded', { - methodCall: 'd.custom=addtime', - transformValue: dateTransformer, -}); - -torrentListPropMap.set('dateCreated', { - methodCall: 'd.creation_date=', - transformValue: dateTransformer, -}); - -torrentListPropMap.set('throttleName', { - methodCall: 'd.throttle_name=', - transformValue: defaultTransformer, -}); - -torrentListPropMap.set('isMultiFile', { - methodCall: 'd.is_multi_file=', - transformValue: booleanTransformer, -}); - -torrentListPropMap.set('isPrivate', { - methodCall: 'd.is_private=', - transformValue: booleanTransformer, -}); - -torrentListPropMap.set('tags', { - methodCall: 'd.custom1=', - transformValue: value => { - if (value === '') { - return []; - } - - return value - .split(',') - .sort() - .map(tag => decodeURIComponent(tag)); - }, -}); - -torrentListPropMap.set('comment', { - methodCall: 'd.custom2=', - transformValue: value => { - let comment = decodeURIComponent(value); - - if (comment.match(/^VRS24mrker/)) { - comment = comment.substr(10); - } - - return comment; - }, -}); - -torrentListPropMap.set('ignoreScheduler', { - methodCall: 'd.custom=sch_ignore', - transformValue: booleanTransformer, -}); - -torrentListPropMap.set('trackerURIs', { - methodCall: 'cat="$t.multicall=d.hash=,t.url=,cat={|||}"', - transformValue: value => { - const trackers = value.split('|||'); - const trackerDomains = []; - - trackers.forEach(tracker => { - let domain = regEx.domainName.exec(tracker); - - if (domain && domain[1]) { - domain = domain[1]; - - const minSubsetLength = 3; - const domainSubsets = domain.split('.'); - let desiredSubsets = 2; - - if (domainSubsets.length > desiredSubsets) { - const lastDesiredSubset = domainSubsets[domainSubsets.length - desiredSubsets]; - if (lastDesiredSubset.length <= minSubsetLength) { - desiredSubsets++; - } - } - - domain = domainSubsets.slice(desiredSubsets * -1).join('.'); - - trackerDomains.push(domain); - } - }); - - return trackerDomains; - }, -}); - -torrentListPropMap.set('seedsConnected', { - methodCall: 'd.peers_complete=', - transformValue: Number, -}); - -torrentListPropMap.set('seedsTotal', { - methodCall: 'cat="$t.multicall=d.hash=,t.scrape_complete=,cat={|||}"', - transformValue: value => Number(value.substr(0, value.indexOf('|||'))), -}); - -torrentListPropMap.set('peersConnected', { - methodCall: 'd.peers_accounted=', - transformValue: Number, -}); - -torrentListPropMap.set('peersTotal', { - methodCall: 'cat="$t.multicall=d.hash=,t.scrape_incomplete=,cat={|||}"', - transformValue: value => Number(value.substr(0, value.indexOf('|||'))), -}); - -module.exports = torrentListPropMap; diff --git a/server/constants/torrentServiceEvents.js b/server/constants/torrentServiceEvents.js deleted file mode 100644 index 2859e9ea6..000000000 --- a/server/constants/torrentServiceEvents.js +++ /dev/null @@ -1,5 +0,0 @@ -const objectUtil = require('../../shared/util/objectUtil'); - -const torrentServiceEvents = ['FETCH_TORRENT_LIST_ERROR', 'FETCH_TORRENT_LIST_SUCCESS', 'TORRENT_LIST_DIFF_CHANGE']; - -module.exports = objectUtil.createSymbolMapFromArray(torrentServiceEvents); diff --git a/server/constants/transferSummaryPropMap.js b/server/constants/transferSummaryPropMap.js deleted file mode 100644 index 323e61bda..000000000 --- a/server/constants/transferSummaryPropMap.js +++ /dev/null @@ -1,33 +0,0 @@ -const transferSummaryPropMap = new Map(); - -transferSummaryPropMap.set('upRate', { - methodCall: 'throttle.global_up.rate', - transformValue: Number, -}); - -transferSummaryPropMap.set('upTotal', { - methodCall: 'throttle.global_up.total', - transformValue: Number, -}); - -transferSummaryPropMap.set('upThrottle', { - methodCall: 'throttle.global_up.max_rate', - transformValue: Number, -}); - -transferSummaryPropMap.set('downRate', { - methodCall: 'throttle.global_down.rate', - transformValue: Number, -}); - -transferSummaryPropMap.set('downTotal', { - methodCall: 'throttle.global_down.total', - transformValue: Number, -}); - -transferSummaryPropMap.set('downThrottle', { - methodCall: 'throttle.global_down.max_rate', - transformValue: Number, -}); - -module.exports = transferSummaryPropMap; diff --git a/server/middleware/appendUserServices.js b/server/middleware/appendUserServices.js deleted file mode 100644 index 2feb91573..000000000 --- a/server/middleware/appendUserServices.js +++ /dev/null @@ -1,6 +0,0 @@ -const services = require('../services'); - -module.exports = (req, res, next) => { - req.services = services.getAllServices(req.user); - next(); -}; diff --git a/server/middleware/appendUserServices.ts b/server/middleware/appendUserServices.ts new file mode 100644 index 000000000..6ec6ac8aa --- /dev/null +++ b/server/middleware/appendUserServices.ts @@ -0,0 +1,19 @@ +import type {Request, Response, NextFunction} from 'express'; + +import services from '../services'; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Express { + interface Request { + services?: ReturnType; + } + } +} + +export default (req: Request, _res: Response, next: NextFunction) => { + if (req.user != null) { + req.services = services.getAllServices(req.user); + } + next(); +}; diff --git a/server/middleware/booleanCoerce.js b/server/middleware/booleanCoerce.js deleted file mode 100644 index 928a6d3c5..000000000 --- a/server/middleware/booleanCoerce.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = key => (req, res, next) => { - const value = req.body && req.body[key]; - - if (value && typeof value === 'string') { - req.body[key] = value === 'true'; - } - - next(); -}; diff --git a/server/middleware/clientActivityStream.js b/server/middleware/clientActivityStream.js deleted file mode 100644 index 6b8ed7a5e..000000000 --- a/server/middleware/clientActivityStream.js +++ /dev/null @@ -1,150 +0,0 @@ -const clientGatewayServiceEvents = require('../constants/clientGatewayServiceEvents'); -const historyServiceEvents = require('../constants/historyServiceEvents'); -const historySnapshotTypes = require('../../shared/constants/historySnapshotTypes'); -const notificationServiceEvents = require('../constants/notificationServiceEvents'); -const ServerEvent = require('../models/ServerEvent'); -const serverEventTypes = require('../../shared/constants/serverEventTypes'); -const services = require('../services'); -const taxonomyServiceEvents = require('../constants/taxonomyServiceEvents'); -const torrentServiceEvents = require('../constants/torrentServiceEvents'); -const diskUsageServiceEvents = require('../constants/diskUsageServiceEvents'); -const DiskUsageService = require('../services/diskUsageService'); - -module.exports = (req, res) => { - const { - query: {historySnapshot = historySnapshotTypes.FIVE_MINUTE}, - user, - } = req; - - const serviceInstances = services.getAllServices(user); - const serverEvent = new ServerEvent(res); - const taxonomy = serviceInstances.taxonomyService.getTaxonomy(); - const torrentList = serviceInstances.torrentService.getTorrentList(); - const transferSummary = serviceInstances.historyService.getTransferSummary(); - - // Hook into events and stop listening when connection is closed - const handleEvents = (emitter, event, handler) => { - emitter.on(event, handler); - res.on('close', () => { - emitter.removeListener(event, handler); - }); - }; - - // Emit current state immediately on connection. - serverEvent.setID(Date.now()); - serverEvent.setType(serverEventTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE); - serverEvent.addData({isConnected: !serviceInstances.clientGatewayService.hasError}); - serverEvent.emit(); - - const handleDiskUsageChange = diskUsageChange => { - serverEvent.setID(diskUsageChange.id); - serverEvent.setType(serverEventTypes.DISK_USAGE_CHANGE); - serverEvent.addData(diskUsageChange.disks); - serverEvent.emit(); - }; - - DiskUsageService.updateDisks().then(() => { - const diskUsage = DiskUsageService.getDiskUsage(); - serverEvent.setID(diskUsage.id); - serverEvent.setType(serverEventTypes.DISK_USAGE_CHANGE); - serverEvent.addData(diskUsage.disks); - serverEvent.emit(); - handleEvents(DiskUsageService, diskUsageServiceEvents.DISK_USAGE_CHANGE, handleDiskUsageChange); - }); - - serverEvent.setID(torrentList.id); - serverEvent.setType(serverEventTypes.TORRENT_LIST_FULL_UPDATE); - serverEvent.addData(torrentList.torrents); - serverEvent.emit(); - - serverEvent.setID(taxonomy.id); - serverEvent.setType(serverEventTypes.TAXONOMY_FULL_UPDATE); - serverEvent.addData(taxonomy.taxonomy); - serverEvent.emit(); - - serverEvent.setID(transferSummary.id); - serverEvent.setType(serverEventTypes.TRANSFER_SUMMARY_FULL_UPDATE); - serverEvent.addData(transferSummary.transferSummary); - serverEvent.emit(); - - serverEvent.setID(Date.now()); - serverEvent.setType(serverEventTypes.NOTIFICATION_COUNT_CHANGE); - serverEvent.addData(serviceInstances.notificationService.getNotificationCount()); - serverEvent.emit(); - - handleEvents(serviceInstances.clientGatewayService, clientGatewayServiceEvents.CLIENT_CONNECTION_STATE_CHANGE, () => { - serverEvent.setID(Date.now()); - serverEvent.setType(serverEventTypes.CLIENT_CONNECTIVITY_STATUS_CHANGE); - serverEvent.addData({isConnected: !serviceInstances.clientGatewayService.hasError}); - serverEvent.emit(); - }); - - if (serviceInstances.clientGatewayService.hasError) { - serviceInstances.clientGatewayService.testGateway(); - } - - // TODO: Handle empty or sub-optimal history states. - // Get user's specified history snapshot current history. - serviceInstances.historyService.getHistory({snapshot: historySnapshot}, (snapshot, error) => { - const {timestamps: lastTimestamps = []} = snapshot; - const lastTimestamp = lastTimestamps[lastTimestamps.length - 1]; - - if (error == null) { - serverEvent.setID(lastTimestamp); - serverEvent.setType(serverEventTypes.TRANSFER_HISTORY_FULL_UPDATE); - serverEvent.addData(snapshot); - serverEvent.emit(); - } - }); - - // Add user's specified history snapshot change event listener. - handleEvents( - serviceInstances.historyService, - historyServiceEvents[`${historySnapshotTypes[historySnapshot]}_SNAPSHOT_FULL_UPDATE`], - payload => { - const {data, id} = payload; - - serverEvent.setID(id); - serverEvent.setType(serverEventTypes.TRANSFER_HISTORY_FULL_UPDATE); - serverEvent.addData(data); - serverEvent.emit(); - }, - ); - - handleEvents(serviceInstances.notificationService, notificationServiceEvents.NOTIFICATION_COUNT_CHANGE, payload => { - const {data, id} = payload; - - serverEvent.setID(id); - serverEvent.setType(serverEventTypes.NOTIFICATION_COUNT_CHANGE); - serverEvent.addData(data); - serverEvent.emit(); - }); - - // Add diff event listeners. - handleEvents(serviceInstances.historyService, historyServiceEvents.TRANSFER_SUMMARY_DIFF_CHANGE, payload => { - const {diff, id} = payload; - - serverEvent.setID(id); - serverEvent.setType(serverEventTypes.TRANSFER_SUMMARY_DIFF_CHANGE); - serverEvent.addData(diff); - serverEvent.emit(); - }); - - handleEvents(serviceInstances.taxonomyService, taxonomyServiceEvents.TAXONOMY_DIFF_CHANGE, payload => { - const {diff, id} = payload; - - serverEvent.setID(id); - serverEvent.setType(serverEventTypes.TAXONOMY_DIFF_CHANGE); - serverEvent.addData(diff); - serverEvent.emit(); - }); - - handleEvents(serviceInstances.torrentService, torrentServiceEvents.TORRENT_LIST_DIFF_CHANGE, payload => { - const {diff, id} = payload; - - serverEvent.setID(id); - serverEvent.setType(serverEventTypes.TORRENT_LIST_DIFF_CHANGE); - serverEvent.addData(diff); - serverEvent.emit(); - }); -}; diff --git a/server/middleware/clientActivityStream.ts b/server/middleware/clientActivityStream.ts new file mode 100644 index 000000000..9b139b762 --- /dev/null +++ b/server/middleware/clientActivityStream.ts @@ -0,0 +1,129 @@ +import type {Operation} from 'fast-json-patch'; +import type {Request, Response} from 'express'; +import type TypedEmitter from 'typed-emitter'; + +import type {HistorySnapshot} from '@shared/constants/historySnapshotTypes'; + +import DiskUsage from '../models/DiskUsage'; +import ServerEvent from '../models/ServerEvent'; +import services from '../services'; + +import type {DiskUsageSummary} from '../models/DiskUsage'; +import type {TransferHistory} from '../../shared/types/TransferData'; + +export default async (req: Request, res: Response) => { + const { + query: {historySnapshot = 'FIVE_MINUTE'}, + user, + } = req; + + if (user == null) { + return; + } + + const serviceInstances = services.getAllServices(user); + const serverEvent = new ServerEvent(res); + const fetchTorrentList = serviceInstances.torrentService.fetchTorrentList(); + + // Hook into events and stop listening when connection is closed + const handleEvents = >>( + emitter: T, + event: Parameters[0], + handler: Parameters[1], + ) => { + emitter.on(event, handler); + res.on('close', () => { + emitter.removeListener(event, handler); + }); + }; + + // Emit current state immediately on connection. + serverEvent.emit(Date.now(), 'CLIENT_CONNECTIVITY_STATUS_CHANGE', { + isConnected: serviceInstances.clientGatewayService?.errorCount === 0, + }); + + // Don't proceed if client connection setting is unusable + if (serviceInstances.clientGatewayService == null) { + return; + } + + // Client connection status change event + handleEvents(serviceInstances.clientGatewayService, 'CLIENT_CONNECTION_STATE_CHANGE', (isConnected: boolean) => { + serverEvent.emit(Date.now(), 'CLIENT_CONNECTIVITY_STATUS_CHANGE', { + isConnected, + }); + }); + + // Trigger a retry if client connection failed + if (serviceInstances.clientGatewayService.errorCount !== 0) { + serviceInstances.clientGatewayService.testGateway().catch(console.error); + } + + // Transfer history + await serviceInstances.historyService.getHistory({snapshot: historySnapshot}).then( + (snapshot) => { + const {timestamps: lastTimestamps} = snapshot || {timestamps: []}; + const lastTimestamp = lastTimestamps[lastTimestamps.length - 1]; + + if (snapshot != null && lastTimestamp != null) { + serverEvent.emit(lastTimestamp, 'TRANSFER_HISTORY_FULL_UPDATE', snapshot); + } else { + const fallbackHistory: TransferHistory = { + download: [0], + upload: [0], + timestamps: [Date.now()], + }; + serverEvent.emit(Date.now(), 'TRANSFER_HISTORY_FULL_UPDATE', fallbackHistory); + } + }, + () => undefined, + ); + + // Disk usage + const disks = DiskUsage.getDiskUsage(); + serverEvent.emit(disks.id, 'DISK_USAGE_CHANGE', disks.disks); + handleEvents(DiskUsage, 'DISK_USAGE_CHANGE', (diskUsageChange: DiskUsageSummary) => { + serverEvent.emit(diskUsageChange.id, 'DISK_USAGE_CHANGE', diskUsageChange.disks); + }); + + // Torrent list + const torrentList = (await fetchTorrentList) || serviceInstances.torrentService.getTorrentListSummary(); + serverEvent.emit(torrentList.id, 'TORRENT_LIST_FULL_UPDATE', torrentList.torrents); + handleEvents( + serviceInstances.torrentService, + 'TORRENT_LIST_DIFF_CHANGE', + ({id, diff}: {id: number; diff: Operation[]}) => { + serverEvent.emit(id, 'TORRENT_LIST_DIFF_CHANGE', diff); + }, + ); + + // Torrent taxonomy + const taxonomy = serviceInstances.taxonomyService.getTaxonomy(); + serverEvent.emit(taxonomy.id, 'TAXONOMY_FULL_UPDATE', taxonomy.taxonomy); + handleEvents(serviceInstances.taxonomyService, 'TAXONOMY_DIFF_CHANGE', (payload) => { + const {diff, id} = payload; + serverEvent.emit(id, 'TAXONOMY_DIFF_CHANGE', diff); + }); + + // Transfer summary + const transferSummary = serviceInstances.historyService.getTransferSummary(); + serverEvent.emit(transferSummary.id, 'TRANSFER_SUMMARY_FULL_UPDATE', transferSummary.transferSummary); + handleEvents( + serviceInstances.historyService, + 'TRANSFER_SUMMARY_DIFF_CHANGE', + ({id, diff}: {id: number; diff: Operation[]}) => { + serverEvent.emit(id, 'TRANSFER_SUMMARY_DIFF_CHANGE', diff); + }, + ); + + // Notifications + serverEvent.emit( + Date.now(), + 'NOTIFICATION_COUNT_CHANGE', + serviceInstances.notificationService.getNotificationCount(), + ); + handleEvents(serviceInstances.notificationService, 'NOTIFICATION_COUNT_CHANGE', (payload) => { + const {data, id} = payload; + serverEvent.emit(id, 'NOTIFICATION_COUNT_CHANGE', data); + }); +}; diff --git a/server/middleware/eventStream.js b/server/middleware/eventStream.js deleted file mode 100644 index 0e4dbe39f..000000000 --- a/server/middleware/eventStream.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = (req, res, next) => { - req.socket.setKeepAlive(true); - req.socket.setTimeout(0); - - res.set({ - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache', - Connection: 'keep-alive', - 'Access-Control-Allow-Origin': '*', - 'X-Accel-Buffering': 'no', - }); - res.status(200); - res.write('retry: 500\n\n'); - - // Keep the connection open by sending a message every so often. - const keepAliveTimeout = setInterval(() => { - res.write(':keep-alive\n\n'); - res.flush(); - }, 500); - - // cleanup on close - res.on('close', () => { - clearInterval(keepAliveTimeout); - }); - - next(); -}; diff --git a/server/middleware/eventStream.ts b/server/middleware/eventStream.ts new file mode 100644 index 000000000..6fabb7189 --- /dev/null +++ b/server/middleware/eventStream.ts @@ -0,0 +1,29 @@ +import type {Request, Response, NextFunction} from 'express'; + +export default (req: Request, res: Response, next: NextFunction) => { + req.socket.setKeepAlive(true); + req.socket.setTimeout(0); + + res.set({ + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'Cross-Origin-Resource-Policy': 'same-origin', + 'X-Accel-Buffering': 'no', + }); + res.status(200); + res.write('retry: 500\n\n'); + + // Keep the connection open by sending a message every so often. + const keepAliveTimeout = setInterval(() => { + res.write(':keep-alive\n\n'); + res.flush(); + }, 500); + + // cleanup on close + res.on('close', () => { + clearInterval(keepAliveTimeout); + }); + + next(); +}; diff --git a/server/middleware/requireAdmin.js b/server/middleware/requireAdmin.js deleted file mode 100644 index e5a2b2c71..000000000 --- a/server/middleware/requireAdmin.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = (req, res, next) => { - if (req.user == null || !req.user.isAdmin) { - return res - .status(403) - .json({message: 'User is not admin.'}) - .send(); - } - next(); -}; diff --git a/server/middleware/requireAdmin.ts b/server/middleware/requireAdmin.ts new file mode 100644 index 000000000..8a8339760 --- /dev/null +++ b/server/middleware/requireAdmin.ts @@ -0,0 +1,11 @@ +import type {Request, Response, NextFunction} from 'express'; + +import {AccessLevel} from '../../shared/schema/constants/Auth'; + +export default (req: Request, res: Response, next: NextFunction) => { + if (req.user == null || req.user.level !== AccessLevel.ADMINISTRATOR) { + res.status(403).json({message: 'User is not admin.'}).send(); + return; + } + next(); +}; diff --git a/server/models/ClientRequest.js b/server/models/ClientRequest.js deleted file mode 100644 index 3b6e8fa48..000000000 --- a/server/models/ClientRequest.js +++ /dev/null @@ -1,386 +0,0 @@ -/** - * This file is deprecated in favor of clientGatewayService. - */ -const mkdirp = require('mkdirp'); -const mv = require('mv'); -const path = require('path'); -const util = require('util'); - -const clientSettingsMap = require('../../shared/constants/clientSettingsMap'); -const rTorrentPropMap = require('../util/rTorrentPropMap'); -const torrentStatusMap = require('../../shared/constants/torrentStatusMap'); - -const addTagsToRequest = (tagsArr, requestParameters) => { - if (tagsArr && tagsArr.length) { - const tags = tagsArr - .reduce((accumulator, currentTag) => { - const tag = encodeURIComponent(currentTag.trim()); - - if (tag !== '' && accumulator.indexOf(tag) === -1) { - accumulator.push(tag); - } - - return accumulator; - }, []) - .join(','); - - requestParameters.push(`d.custom1.set="${tags}"`); - } - - return requestParameters; -}; - -const getEnsuredArray = item => { - if (!util.isArray(item)) { - return [item]; - } - return item; -}; - -const getMethodCall = (methodName, params) => { - params = params || []; - return {methodName, params}; -}; - -class ClientRequest { - constructor(user, services, options) { - options = options || {}; - - this.services = services; - this.user = user; - this.clientRequestManager = this.services.clientRequestManager; - - this.onCompleteFn = null; - this.postProcessFn = null; - this.requests = []; - - if (options.onComplete) { - this.onCompleteFn = options.onComplete; - } - - if (options.postProcess) { - this.postProcessFn = options.postProcess; - } - - if (options.name) { - this.name = options.name; - } - } - - // TODO: Move this to util, doesn't belong here - createDirectory(options) { - if (options.path) { - mkdirp(options.path, error => { - if (error) { - console.trace('Error creating directory.', error); - } - }); - } - } - - clearRequestQueue() { - this.requests = []; - } - - handleError(error) { - if (error.code === 'ECONNREFUSED') { - console.error( - `Connection refused at ${error.address}${error.port ? `:${error.port}` : ''}. ` + - 'Check these values in config.js and ensure that rTorrent is running.', - ); - } - - this.clearRequestQueue(); - - if (this.onCompleteFn) { - this.onCompleteFn(null, error); - } - } - - handleSuccess(data) { - let response = data; - - this.clearRequestQueue(); - - if (this.postProcessFn) { - response = this.postProcessFn(data); - } - - if (this.onCompleteFn) { - this.onCompleteFn(response); - } - } - - onComplete(fn) { - this.onCompleteFn = fn; - } - - postProcess(fn) { - this.postProcessFn = fn; - } - - send() { - const handleSuccess = this.handleSuccess.bind(this); - const handleError = this.handleError.bind(this); - - this.clientRequestManager - .methodCall('system.multicall', [this.requests]) - .then(handleSuccess) - .catch(handleError); - } - - // TODO: Separate these and add support for additional clients. - // rTorrent method calls. - addFiles(options) { - const files = getEnsuredArray(options.files); - const {path: destinationPath, isBasePath, start, tags: tagsArr} = options; - - files.forEach(file => { - let methodCall = 'load.raw_start'; - let parameters = ['', file.buffer]; - const timeAdded = Math.floor(Date.now() / 1000); - - if (destinationPath) { - if (isBasePath) { - parameters.push(`d.directory_base.set="${destinationPath}"`); - } else { - parameters.push(`d.directory.set="${destinationPath}"`); - } - } - - parameters = addTagsToRequest(tagsArr, parameters); - - parameters.push(`d.custom.set=x-filename,${file.originalname}`); - parameters.push(`d.custom.set=addtime,${timeAdded}`); - - // The start value is a string because it was appended to a FormData - // object. - if (start === 'false') { - methodCall = 'load.raw'; - } - - this.requests.push(getMethodCall(methodCall, parameters)); - }); - } - - addURLs(options) { - const {path: destinationPath, isBasePath, start, tags: tagsArr} = options; - const urls = getEnsuredArray(options.urls); - - urls.forEach(url => { - let methodCall = 'load.start'; - let parameters = ['', url]; - const timeAdded = Math.floor(Date.now() / 1000); - - if (destinationPath) { - if (isBasePath) { - parameters.push(`d.directory_base.set="${destinationPath}"`); - } else { - parameters.push(`d.directory.set="${destinationPath}"`); - } - } - - parameters = addTagsToRequest(tagsArr, parameters); - - parameters.push(`d.custom.set=addtime,${timeAdded}`); - - if (!start) { - methodCall = 'load.normal'; - } - - this.requests.push(getMethodCall(methodCall, parameters)); - }); - } - - checkHash(options) { - const {torrentService} = this.services; - const hashes = getEnsuredArray(options.hashes); - const stoppedHashes = hashes.filter(hash => - torrentService.getTorrent(hash).status.includes(torrentStatusMap.stopped), - ); - - const hashesToStart = []; - - this.stopTorrents({hashes}); - - hashes.forEach(hash => { - this.requests.push(getMethodCall('d.check_hash', [hash])); - - if (!stoppedHashes.includes(hash)) { - hashesToStart.push(hash); - } - }); - - if (hashesToStart.length) { - this.startTorrents({hashes: hashesToStart}); - } - } - - fetchSettings(options) { - let {requestedSettings} = options; - - if (requestedSettings == null) { - requestedSettings = clientSettingsMap.defaults.map(settingsKey => clientSettingsMap[settingsKey]); - } - - // Ensure client's response gets mapped to the correct requested keys. - if (options.setRequestedKeysArr) { - options.setRequestedKeysArr(requestedSettings); - } - - requestedSettings.forEach(settingsKey => { - this.requests.push(getMethodCall(settingsKey)); - }); - } - - getTorrentDetails(options) { - const peerParams = [options.hash, ''].concat(options.peerProps); - const fileParams = [options.hash, ''].concat(options.fileProps); - const trackerParams = [options.hash, ''].concat(options.trackerProps); - - this.requests.push(getMethodCall('p.multicall', peerParams)); - this.requests.push(getMethodCall('f.multicall', fileParams)); - this.requests.push(getMethodCall('t.multicall', trackerParams)); - } - - getTorrentList(options) { - this.requests.push(getMethodCall('d.multicall2', options.props)); - } - - getTransferData() { - Object.keys(rTorrentPropMap.transferData).forEach(key => { - this.requests.push(getMethodCall(rTorrentPropMap.transferData[key])); - }); - } - - listMethods(options) { - const args = getEnsuredArray(options.args); - this.requests.push(getMethodCall(options.method, [args])); - } - - moveTorrents(options) { - const {destinationPath} = options; - const filenames = getEnsuredArray(options.filenames); - const sourcePaths = getEnsuredArray(options.sourcePaths); - - sourcePaths.forEach((source, index) => { - let callback = () => {}; - const destination = `${destinationPath}${path.sep}${filenames[index]}`; - const isLastRequest = index + 1 === sourcePaths.length; - - if (isLastRequest) { - callback = this.handleSuccess.bind(this); - } - - if (source !== destination) { - mv(source, destination, {mkdirp: true}, callback); - } else if (isLastRequest) { - callback(); - } - }); - } - - setDownloadPath(options) { - const hashes = getEnsuredArray(options.hashes); - - let pathMethod; - if (options.isBasePath) { - pathMethod = 'd.directory_base.set'; - } else { - pathMethod = 'd.directory.set'; - } - - hashes.forEach(hash => { - this.requests.push(getMethodCall(pathMethod, [hash, options.path])); - this.requests.push(getMethodCall('d.open', [hash])); - this.requests.push(getMethodCall('d.close', [hash])); - }); - } - - setFilePriority(options) { - const fileIndices = getEnsuredArray(options.fileIndices); - const hashes = getEnsuredArray(options.hashes); - - hashes.forEach(hash => { - fileIndices.forEach(fileIndex => { - this.requests.push(getMethodCall('f.priority.set', [`${hash}:f${fileIndex}`, options.priority])); - }); - this.requests.push(getMethodCall('d.update_priorities', [hash])); - }); - } - - setPriority(options) { - const hashes = getEnsuredArray(options.hashes); - - hashes.forEach(hash => { - this.requests.push(getMethodCall('d.priority.set', [hash, options.priority])); - this.requests.push(getMethodCall('d.update_priorities', [hash])); - }); - } - - setSettings(options) { - const settings = getEnsuredArray(options.settings); - - settings.forEach(setting => { - if (setting.overrideLocalSetting) { - this.requests.push(getMethodCall(setting.id, setting.data)); - } else { - this.requests.push(getMethodCall(`${clientSettingsMap[setting.id]}.set`, ['', setting.data])); - } - }); - } - - setTaxonomy(options) { - const methodName = 'd.custom1.set'; - - const tags = options.tags - .reduce((memo, currentTag) => { - const tag = encodeURIComponent(currentTag.trim()); - - if (tag !== '' && memo.indexOf(tag) === -1) { - memo.push(tag); - } - - return memo; - }, []) - .join(','); - - getEnsuredArray(options.hashes).forEach(hash => { - this.requests.push(getMethodCall(methodName, [hash, tags])); - }); - } - - setThrottle(options) { - let methodName = 'throttle.global_down.max_rate.set'; - if (options.direction === 'upload') { - methodName = 'throttle.global_up.max_rate.set'; - } - this.requests.push(getMethodCall(methodName, ['', options.throttle])); - } - - startTorrents(options) { - if (!options.hashes) { - console.error("startTorrents requires key 'hashes'."); - return; - } - - getEnsuredArray(options.hashes).forEach(hash => { - this.requests.push(getMethodCall('d.open', [hash])); - this.requests.push(getMethodCall('d.start', [hash])); - }); - } - - stopTorrents(options) { - if (!options.hashes) { - console.error("stopTorrents requires key 'hashes'."); - return; - } - - getEnsuredArray(options.hashes).forEach(hash => { - this.requests.push(getMethodCall('d.stop', [hash])); - this.requests.push(getMethodCall('d.close', [hash])); - }); - } -} - -module.exports = ClientRequest; diff --git a/server/models/DiskUsage.ts b/server/models/DiskUsage.ts new file mode 100644 index 000000000..b2eba16b5 --- /dev/null +++ b/server/models/DiskUsage.ts @@ -0,0 +1,83 @@ +import {EventEmitter} from 'events'; +import type TypedEmitter from 'typed-emitter'; + +import type {Disks} from '@shared/types/DiskUsage'; + +import {isPlatformSupported, diskUsage} from '../util/diskUsageUtil'; + +import type {SupportedPlatform} from '../util/diskUsageUtil'; + +export interface DiskUsageSummary { + id: number; + disks: Disks; +} + +interface DiskUsageEvents { + DISK_USAGE_CHANGE: (usage: DiskUsageSummary) => void; + newListener: (event: keyof Omit) => void; + removeListener: (event: keyof Omit) => void; +} + +const INTERVAL_UPDATE = 10000; + +class DiskUsage extends (EventEmitter as new () => TypedEmitter) { + disks: Disks = []; + tLastChange = 0; + interval = 0; + updateInterval?: NodeJS.Timeout; + + constructor() { + super(); + + if (!isPlatformSupported()) { + console.log(`warning: DiskUsageService does not support this platform`); + return; + } + + // start polling disk usage when the first listener is added + this.on('newListener', (event) => { + if (this.listenerCount('DISK_USAGE_CHANGE') === 0 && event === 'DISK_USAGE_CHANGE') { + this.updateInterval = setInterval(this.updateDisks, INTERVAL_UPDATE); + } + }); + + // stop polling disk usage when the last listener is removed + this.on('removeListener', (event) => { + if ( + this.listenerCount('DISK_USAGE_CHANGE') === 0 && + event === 'DISK_USAGE_CHANGE' && + this.updateInterval != null + ) { + clearInterval(this.updateInterval); + } + }); + } + + updateDisks = () => { + if (!isPlatformSupported()) { + return Promise.reject(); + } + + return diskUsage[process.platform as SupportedPlatform]() + .then((disks) => { + // Mountpoints with a very long path are unlikely to be useful. + return disks.filter((disk) => typeof disk.target === 'string' && disk.target.length < 30); + }) + .then((disks) => { + if (disks.length !== this.disks.length || disks.some((d, i) => d.used !== this.disks[i].used)) { + this.tLastChange = Date.now(); + this.disks = disks; + this.emit('DISK_USAGE_CHANGE', this.getDiskUsage()); + } + }); + }; + + getDiskUsage(): DiskUsageSummary { + return { + id: this.tLastChange, + disks: this.disks, + } as const; + } +} + +export default new DiskUsage(); diff --git a/server/models/Feed.js b/server/models/Feed.js deleted file mode 100644 index f0868af1a..000000000 --- a/server/models/Feed.js +++ /dev/null @@ -1,74 +0,0 @@ -const FeedSub = require('feedsub'); - -class Feed { - constructor(options) { - this.options = options || {}; - this.options.maxItemHistory = options.maxItemHistory || 100; - this.items = []; - - if (!options.url) { - console.error('Feed URL must be defined.'); - return null; - } - - this.reader = new FeedSub(options.url, { - autoStart: true, - emitOnStart: true, - maxHistory: this.options.maxItemHistory, - interval: options.interval ? Number(options.interval) : 15, - forceInterval: true, - readEveryItem: true, - }); - - this.initReader(); - } - - modify(options) { - Object.assign(this.options, options); - this.items = []; - - this.reader = new FeedSub(options.url, { - autoStart: true, - emitOnStart: true, - maxHistory: this.options.maxItemHistory, - interval: options.interval ? Number(options.interval) : 15, - forceInterval: true, - readEveryItem: true, - }); - - this.initReader(); - } - - getItems() { - return this.items; - } - - handleFeedItems(items) { - const nextLength = this.items.length + items.length; - if (nextLength >= this.options.maxItemHistory) { - const diff = nextLength - this.options.maxHistory; - this.items = this.items.splice(0, diff); - } - - this.items = this.items.concat(items); - - this.options.onNewItems({ - feed: this.options, - items, - }); - } - - initReader() { - this.reader.on('items', this.handleFeedItems.bind(this)); - this.reader.on('error', error => { - console.log('Feed reader error:', error); - }); - this.reader.start(); - } - - stopReader() { - this.reader.stop(); - } -} - -module.exports = Feed; diff --git a/server/models/FeedReader.ts b/server/models/FeedReader.ts new file mode 100644 index 000000000..ffacecb05 --- /dev/null +++ b/server/models/FeedReader.ts @@ -0,0 +1,74 @@ +import FeedSub, {FeedItem} from 'feedsub'; + +export interface FeedReaderOptions { + feedID: string; + feedLabel: string; + url: string; + interval: number; + maxHistory: number; + onNewItems: (options: FeedReaderOptions, items: Array) => void; +} + +class FeedReader { + private options: FeedReaderOptions; + private items: Array = []; + private reader: FeedSub | null = null; + + constructor(options: FeedReaderOptions) { + this.options = options; + + this.initReader(); + } + + modify(options: Partial) { + this.options = {...this.options, ...options}; + this.items = []; + + this.initReader(); + } + + getOptions() { + return this.options; + } + + getItems() { + return this.items; + } + + handleFeedItems = (items: Array) => { + const nextLength = this.items.length + items.length; + if (nextLength >= this.options.maxHistory) { + const diff = nextLength - this.options.maxHistory; + this.items = this.items.splice(0, diff); + } + + this.items = this.items.concat(items); + + this.options.onNewItems(this.options, items); + }; + + initReader() { + this.reader = new FeedSub(this.options.url, { + autoStart: true, + emitOnStart: true, + maxHistory: this.options.maxHistory, + interval: this.options.interval, + forceInterval: true, + }); + + this.reader.on('items', this.handleFeedItems); + this.reader.on('error', (error) => { + console.log('Feed reader error:', error); + }); + this.reader.start(); + } + + stopReader() { + if (this.reader != null) { + this.reader.stop(); + this.reader = null; + } + } +} + +export default FeedReader; diff --git a/server/models/Filesystem.js b/server/models/Filesystem.js deleted file mode 100644 index 6cf2569bd..000000000 --- a/server/models/Filesystem.js +++ /dev/null @@ -1,37 +0,0 @@ -const fs = require('fs'); -const ospath = require('ospath'); -const path = require('path'); - -const getDirectoryList = (options, callback) => { - const sourcePath = (options.path || '/').replace(/^~/, ospath.home()); - - try { - const directories = []; - const files = []; - - fs.readdirSync(sourcePath).forEach(item => { - const joinedPath = path.join(sourcePath, item); - if (fs.existsSync(joinedPath)) { - if (fs.statSync(joinedPath).isDirectory()) { - directories.push(item); - } else { - files.push(item); - } - } - }); - - const hasParent = /^.{0,}:?(\/|\\){1,1}\S{1,}/.test(sourcePath); - - callback({ - directories, - files, - hasParent, - path: sourcePath, - separator: path.sep, - }); - } catch (error) { - callback(null, error); - } -}; - -module.exports = {getDirectoryList}; diff --git a/server/models/HistoryEra.js b/server/models/HistoryEra.js deleted file mode 100644 index 67b278917..000000000 --- a/server/models/HistoryEra.js +++ /dev/null @@ -1,205 +0,0 @@ -const Datastore = require('nedb'); -const path = require('path'); - -const config = require('../../config'); - -const MAX_NEXT_ERA_UPDATE_INTERVAL = 1000 * 60 * 60 * 12; // 12 hours -const CUMULATIVE_DATA_BUFFER_DIFF = 500; // 500 miliseconds -const REQUIRED_FIELDS = ['interval', 'maxTime', 'name']; - -const hasRequiredFields = opts => { - let requirementsMet = true; - - REQUIRED_FIELDS.forEach(field => { - if (opts[field] == null) { - console.error(`HistoryEra requires ${field}`); - requirementsMet = false; - } - }); - - return requirementsMet; -}; - -class HistoryEra { - constructor(user, opts) { - opts = opts || {}; - - if (!hasRequiredFields(opts)) { - return; - } - - this.data = []; - this.opts = opts; - this.ready = false; - this.user = user; - this.startedAt = Date.now(); - this.db = this.loadDatabase(this.opts.name); - - this.setLastUpdate(this.db); - this.removeOutdatedData(this.db); - - let cleanupInterval = this.opts.maxTime; - let {nextEraUpdateInterval} = this.opts; - - if (cleanupInterval === 0 || cleanupInterval > config.dbCleanInterval) { - cleanupInterval = config.dbCleanInterval; - } - - if (nextEraUpdateInterval && nextEraUpdateInterval > MAX_NEXT_ERA_UPDATE_INTERVAL) { - nextEraUpdateInterval = MAX_NEXT_ERA_UPDATE_INTERVAL; - } - - if (nextEraUpdateInterval) { - this.startNextEraUpdate(nextEraUpdateInterval, this.db); - } - - this.startAutoCleanup(cleanupInterval, this.db); - } - - loadDatabase(dbName) { - const db = new Datastore({ - autoload: true, - filename: path.join(config.dbPath, this.user._id, 'history', `${dbName}.db`), - }); - - this.ready = true; - return db; - } - - addData(data) { - if (!this.ready) { - console.error('database is not ready'); - return; - } - - const currentTime = Date.now(); - - if (currentTime - this.lastUpdate >= this.opts.interval - CUMULATIVE_DATA_BUFFER_DIFF) { - this.lastUpdate = currentTime; - - this.db.insert({ - ts: currentTime, - up: Number(data.upload), - dn: Number(data.download), - }); - } else { - this.db.find({ts: this.lastUpdate}, (err, docs) => { - if (docs.length !== 0) { - const doc = docs[0]; - const numUpdates = Number(doc.num || 1); - const currentDownAvg = Number(doc.dn); - const currentUpAvg = Number(doc.up); - - const downAvg = ((currentDownAvg * numUpdates + Number(data.download)) / (numUpdates + 1)).toFixed(1); - const upAvg = ((currentUpAvg * numUpdates + Number(data.upload)) / (numUpdates + 1)).toFixed(1); - - // TODO: Remove this nonsense, I think this bug is resolved. - if (downAvg == null || upAvg == null) { - console.error('\n\n'); - console.error('Warning: null values set in database!'); - console.error(`DB: ${this.opts.name}`); - console.error( - `numUpdates: ${numUpdates} -currentDownAvg: ${currentDownAvg} -currentUpAvg: ${currentUpAvg} -downAvg: ${downAvg} -upAvg: ${upAvg}`, - ); - console.error('\n\n'); - } - - this.db.update( - { - ts: this.lastUpdate, - }, - { - ts: this.lastUpdate, - up: Number(upAvg), - dn: Number(downAvg), - num: numUpdates + 1, - }, - ); - } - }); - } - } - - cleanup(db) { - this.removeOutdatedData(db); - db.persistence.compactDatafile(); - } - - getData(opts, callback) { - const minTimestamp = Date.now() - this.opts.maxTime; - - this.db - .find({ts: {$gte: minTimestamp}}) - .sort({ts: 1}) - .exec((err, docs) => { - if (err) { - callback(null, err); - return; - } - - callback(docs); - }); - } - - removeOutdatedData(db) { - if (this.opts.maxTime > 0) { - const minTimestamp = Date.now() - this.opts.maxTime; - db.remove({ts: {$lt: minTimestamp}}, {multi: true}); - } - } - - setLastUpdate(db) { - let lastUpdate = 0; - - db.find({}, (err, docs) => { - docs.forEach(doc => { - if (doc.ts > lastUpdate) { - lastUpdate = doc.ts; - } - }); - this.lastUpdate = lastUpdate; - }); - } - - startAutoCleanup(interval, db) { - this.autoCleanupInterval = setInterval(this.cleanup.bind(this, db), interval); - } - - startNextEraUpdate(interval, currentDB, nextDB) { - this.nextEraUpdateInterval = setInterval(this.updateNextEra.bind(this, currentDB, nextDB), interval); - } - - stopAutoCleanup() { - clearInterval(this.autoCleanupInterval); - this.autoCleanupInterval = null; - } - - stopNextEraUpdate() { - clearInterval(this.nextEraUpdateInterval); - this.nextEraUpdateInterval = null; - } - - updateNextEra(currentDB) { - const minTimestamp = Date.now() - this.opts.nextEraUpdateInterval; - currentDB.find({ts: {$gte: minTimestamp}}, (err, docs) => { - let downTotal = 0; - let upTotal = 0; - - docs.forEach(doc => { - downTotal += Number(doc.dn); - upTotal += Number(doc.up); - }); - - this.opts.nextEra.addData({ - download: Number(downTotal / docs.length).toFixed(1), - upload: Number(upTotal / docs.length).toFixed(1), - }); - }); - } -} - -module.exports = HistoryEra; diff --git a/server/models/HistoryEra.ts b/server/models/HistoryEra.ts new file mode 100644 index 000000000..a74ba7b7b --- /dev/null +++ b/server/models/HistoryEra.ts @@ -0,0 +1,171 @@ +import type {UserInDatabase} from '@shared/schema/Auth'; +import type {TransferData, TransferSnapshot} from '@shared/types/TransferData'; + +import Datastore from 'nedb-promises'; +import path from 'path'; + +import config from '../../config'; + +interface HistoryEraOpts { + interval: number; + maxTime: number; + name: string; + nextEraUpdateInterval?: number; + nextEra?: HistoryEra; +} + +const MAX_NEXT_ERA_UPDATE_INTERVAL = 1000 * 60 * 60 * 12; // 12 hours +const CUMULATIVE_DATA_BUFFER_DIFF = 500; // 500 milliseconds + +class HistoryEra { + data = []; + ready: Promise; + lastUpdate = 0; + startedAt = Date.now(); + opts: HistoryEraOpts; + db: Datastore; + autoCleanupInterval?: NodeJS.Timeout; + nextEraUpdateInterval?: NodeJS.Timeout; + + constructor(user: UserInDatabase, opts: HistoryEraOpts) { + this.opts = opts; + this.db = Datastore.create({ + autoload: true, + filename: path.join(config.dbPath, user._id, 'history', `${opts.name}.db`), + }); + this.ready = this.prepareDatabase(); + } + + private async prepareDatabase(): Promise { + let lastUpdate = 0; + + await this.db.find({}).then( + (snapshots) => { + snapshots.forEach((snapshot) => { + if (snapshot.timestamp > lastUpdate) { + lastUpdate = snapshot.timestamp; + } + }); + + this.lastUpdate = lastUpdate; + }, + () => undefined, + ); + + await this.removeOutdatedData(); + + let cleanupInterval = this.opts.maxTime; + + if (cleanupInterval === 0 || cleanupInterval > config.dbCleanInterval) { + cleanupInterval = config.dbCleanInterval; + } + + this.autoCleanupInterval = setInterval(this.removeOutdatedData, cleanupInterval); + } + + private removeOutdatedData = async (): Promise => { + if (this.opts.maxTime > 0) { + const minTimestamp = Date.now() - this.opts.maxTime; + return this.db.remove({timestamp: {$lt: minTimestamp}}, {multi: true}).then( + () => undefined, + () => undefined, + ); + } + }; + + private updateNextEra = async (): Promise => { + if (this.opts.nextEraUpdateInterval == null) { + return; + } + + const minTimestamp = Date.now() - this.opts.nextEraUpdateInterval; + + return this.db + .find({timestamp: {$gte: minTimestamp}}) + .then((snapshots) => { + if (this.opts.nextEra == null) { + return; + } + + let downTotal = 0; + let upTotal = 0; + + snapshots.forEach((snapshot) => { + downTotal += Number(snapshot.download); + upTotal += Number(snapshot.upload); + }); + + this.opts.nextEra.addData({ + download: Number(Number(downTotal / snapshots.length).toFixed(1)), + upload: Number(Number(upTotal / snapshots.length).toFixed(1)), + }); + }); + }; + + async addData(data: TransferData): Promise { + await this.ready; + + const currentTime = Date.now(); + + if (currentTime - this.lastUpdate >= this.opts.interval - CUMULATIVE_DATA_BUFFER_DIFF) { + this.lastUpdate = currentTime; + await this.db + .insert({ + timestamp: currentTime, + ...data, + }) + .catch(() => undefined); + } else { + await this.db + .find({timestamp: this.lastUpdate}) + .then( + async (snapshots) => { + if (snapshots.length !== 0) { + const snapshot = snapshots[0]; + const numUpdates = snapshot.numUpdates || 1; + + // calculate average and update + const updatedSnapshot: TransferSnapshot = { + timestamp: this.lastUpdate, + upload: Number(((snapshot.upload * numUpdates + data.upload) / (numUpdates + 1)).toFixed(1)), + download: Number(((snapshot.download * numUpdates + data.download) / (numUpdates + 1)).toFixed(1)), + numUpdates: numUpdates + 1, + }; + + await this.db.update({timestamp: this.lastUpdate}, updatedSnapshot).catch(() => undefined); + } + }, + () => undefined, + ); + } + } + + async getData(): Promise { + await this.ready; + + const minTimestamp = Date.now() - this.opts.maxTime; + + return this.db + .find({timestamp: {$gte: minTimestamp}}) + .sort({timestamp: 1}) + .then((snapshots) => snapshots.slice(snapshots.length - config.maxHistoryStates)); + } + + async setNextEra(nextEra: HistoryEra): Promise { + await this.ready; + + this.opts.nextEra = nextEra; + + let {nextEraUpdateInterval} = this.opts; + + if (nextEraUpdateInterval && nextEraUpdateInterval > MAX_NEXT_ERA_UPDATE_INTERVAL) { + nextEraUpdateInterval = MAX_NEXT_ERA_UPDATE_INTERVAL; + } + + if (nextEraUpdateInterval) { + this.nextEraUpdateInterval = setInterval(this.updateNextEra, nextEraUpdateInterval); + } + } +} + +export default HistoryEra; diff --git a/server/models/ServerEvent.js b/server/models/ServerEvent.js deleted file mode 100644 index fbc140342..000000000 --- a/server/models/ServerEvent.js +++ /dev/null @@ -1,32 +0,0 @@ -class ServerEvent { - constructor(res) { - this.data = ''; - this.res = res; - - // Add 2kb padding for IE. - const padding = new Array(2049); - res.write(`:${padding.join(' ')}\n`); - } - - addData(data) { - const lines = JSON.stringify(data).split(/\n/); - - this.data = lines.reduce((accumulator, datum) => `${this.data}data:${datum}\n`, this.data); - } - - emit() { - this.res.write(`${this.data}\n`); - this.res.flush(); - this.data = ''; - } - - setID(id) { - this.res.write(`id:${id}\n`); - } - - setType(eventType) { - this.res.write(`event:${eventType}\n`); - } -} - -module.exports = ServerEvent; diff --git a/server/models/ServerEvent.ts b/server/models/ServerEvent.ts new file mode 100644 index 000000000..9a8efafc6 --- /dev/null +++ b/server/models/ServerEvent.ts @@ -0,0 +1,20 @@ +import type {Response} from 'express'; + +import type {ServerEvents} from '@shared/types/ServerEvents'; + +class ServerEvent { + res: Response; + + constructor(res: Response) { + this.res = res; + } + + emit(id: number, eventType: T, data: ServerEvents[T]) { + this.res.write(`id:${id}\n`); + this.res.write(`event:${eventType}\n`); + this.res.write(`data:${JSON.stringify(data)}\n\n`); + this.res.flush(); + } +} + +export default ServerEvent; diff --git a/server/models/TemporaryStorage.js b/server/models/TemporaryStorage.js deleted file mode 100644 index 778c74241..000000000 --- a/server/models/TemporaryStorage.js +++ /dev/null @@ -1,21 +0,0 @@ -const fs = require('fs'); -const mkdirp = require('mkdirp'); -const path = require('path'); - -class TemporaryStorage { - constructor() { - this.tempPath = path.join(path.dirname(require.main.filename), '..', 'temp'); - - mkdirp(this.tempPath); - } - - deleteFile(filename) { - fs.unlink(this.getTempPath(filename)); - } - - getTempPath(filename) { - return path.join(this.tempPath, filename); - } -} - -module.exports = new TemporaryStorage(); diff --git a/server/models/TemporaryStorage.ts b/server/models/TemporaryStorage.ts new file mode 100644 index 000000000..e07d2f26e --- /dev/null +++ b/server/models/TemporaryStorage.ts @@ -0,0 +1,14 @@ +import fs from 'fs'; +import path from 'path'; + +import {tempPath} from '../../config'; + +fs.mkdirSync(tempPath, {recursive: true}); + +export const getTempPath = (filename: string): string => { + return path.join(tempPath, filename); +}; + +export const deleteFile = (filename: string): void => { + fs.unlinkSync(getTempPath(filename)); +}; diff --git a/server/models/Users.js b/server/models/Users.js deleted file mode 100644 index 4ea624d53..000000000 --- a/server/models/Users.js +++ /dev/null @@ -1,169 +0,0 @@ -const argon2 = require('argon2'); -const Datastore = require('nedb'); -const fs = require('fs-extra'); -const path = require('path'); - -const config = require('../../config'); -const services = require('../services'); - -class Users { - constructor() { - this.ready = false; - this.db = this.loadDatabase(); - } - - bootstrapServicesForAllUsers() { - this.listUsers((users, err) => { - if (err) throw err; - if (users && users.length) { - users.forEach(services.bootstrapServicesForUser); - } - }); - } - - comparePassword(credentials, callback) { - this.db.findOne({username: credentials.username}).exec((err, user) => { - if (err) { - return callback(null, err); - } - - // Username not found. - if (user == null) { - return callback(null, user); - } - - argon2 - .verify(user.password, credentials.password) - .then(argon2Match => { - if (argon2Match) { - return callback(argon2Match, user.isAdmin); - } - - callback(null, argon2Match, false); - }) - .catch(error => callback(null, error)); - }); - } - - createUser(credentials, callback) { - const {password, username, host, port, socketPath, isAdmin} = credentials; - - if (!this.ready) { - return callback(null, 'Users database is not ready.'); - } - - if (username === '' || username == null) { - return callback(null, 'Username cannot be empty.'); - } - - argon2 - .hash(password) - .then(hash => { - this.db.insert( - { - username, - password: hash, - host, - port, - socketPath, - isAdmin, - }, - (error, user) => { - if (error) { - if (error.errorType === 'uniqueViolated') { - error = 'Username already exists.'; - } - - return callback(null, error); - } - - services.bootstrapServicesForUser(user); - - return callback({username}); - }, - ); - }) - .catch(error => { - callback(null, error); - }); - } - - removeUser(username, callback) { - this.db.findOne({username}).exec((findError, user) => { - if (findError) { - return callback(null, findError); - } - - // Username not found. - if (user == null) { - return callback(null, user); - } - - this.db.remove({username}, {}, removeError => { - if (removeError) { - return callback(null, removeError); - } - - fs.removeSync(path.join(config.dbPath, user._id)); - - return callback({username}); - }); - }); - } - - updateUser(username, userRecordPatch, callback) { - this.db.update({username}, {$set: userRecordPatch}, (err, numUsersUpdated, updatedUser) => { - if (err) return callback(null, err); - // Username not found. - if (numUsersUpdated === 0) { - return callback(null, err); - } - - return callback(updatedUser); - }); - } - - initialUserGate(handlers) { - this.db.find({}, (err, users) => { - if (users && users.length > 0) { - return handlers.handleSubsequentUser(); - } - - return handlers.handleInitialUser(); - }); - } - - loadDatabase() { - const db = new Datastore({ - autoload: true, - filename: path.join(config.dbPath, 'users.db'), - }); - - db.ensureIndex({fieldName: 'username', unique: true}); - - this.ready = true; - return db; - } - - lookupUser(credentials, callback) { - this.db.findOne({username: credentials.username}, (err, user) => { - if (err) { - return callback(err); - } - - return callback(null, user); - }); - } - - listUsers(callback) { - this.db.find({}, (err, users) => { - if (err) { - return callback(null, err); - } - - return callback(users); - }); - } -} - -module.exports = new Users(); diff --git a/server/models/Users.ts b/server/models/Users.ts new file mode 100644 index 000000000..0cb04f2ba --- /dev/null +++ b/server/models/Users.ts @@ -0,0 +1,198 @@ +import {argon2id, argon2Verify} from 'hash-wasm'; +import crypto from 'crypto'; +import Datastore from 'nedb-promises'; +import fs from 'fs'; +import path from 'path'; + +import {AccessLevel} from '../../shared/schema/constants/Auth'; +import config from '../../config'; +import services from '../services'; + +import type {ClientConnectionSettings} from '../../shared/schema/ClientConnectionSettings'; +import type {Credentials, UserInDatabase} from '../../shared/schema/Auth'; + +const hashPassword = async (password: string): Promise => { + return argon2id({ + password: password, + salt: crypto.randomBytes(16), + parallelism: 1, + iterations: 256, + memorySize: 512, + hashLength: 32, + outputType: 'encoded', + }); +}; + +class Users { + private db = (() => { + const db = Datastore.create({ + autoload: true, + filename: path.join(config.dbPath, 'users.db'), + }); + + db.ensureIndex({fieldName: 'username', unique: true}); + + return db; + })(); + + private configUser: UserInDatabase = { + _id: '_config', + username: '_config', + password: '', + client: config.configUser as ClientConnectionSettings, + level: AccessLevel.ADMINISTRATOR, + }; + + getConfigUser(): Readonly { + return this.configUser; + } + + async bootstrapServicesForAllUsers(): Promise { + return this.listUsers() + .then((users) => Promise.all(users.map((user) => services.bootstrapServicesForUser(user)))) + .then(() => undefined); + } + + /** + * Validates the provided password against the hashed password in database + * + * @param {Pick} credentials - Username and password + * @return {Promise} - Returns access level of the user if matched or rejects with error. + */ + async comparePassword(credentials: Pick): Promise { + return this.db + .findOne({username: credentials.username}) + .then((user) => { + // Wrong data provided + if (credentials?.password == null) { + throw new Error(); + } + + // Username not found. + if (user == null) { + throw new Error(); + } + + return argon2Verify({ + password: credentials.password, + hash: user.password, + }).then((isMatch) => { + if (isMatch) { + return user.level; + } else { + throw new Error(); + } + }); + }); + } + + /** + * Creates a new user. + * Note that validation function always expects an argon2 hash. + * + * @param {Credentials} credentials - Full credentials of a user. + * @param {boolean} shouldHash - Should the password be hashed or stored as-is. + * @return {Promise} - Returns the created user or rejects with error. + */ + async createUser(credentials: Credentials, shouldHash = true): Promise { + const hashed = shouldHash ? await hashPassword(credentials.password).catch(() => undefined) : credentials.password; + + if (this.db == null || hashed == null) { + throw new Error(); + } + + return this.db + .insert({ + ...credentials, + password: hashed, + }) + .catch((err) => { + if (err.message.includes('violates the unique constraint')) { + throw new Error('Username already exists.'); + } + + throw new Error(); + }); + } + + /** + * Removes a user. + * + * @param {string} username - Name of the user to be removed. + * @return {Promise} - Returns ID of removed user or rejects with error. + */ + async removeUser(username: string): Promise { + return this.db + .findOne({username}) + .then(async (user) => { + await this.db.remove({username}, {}); + fs.rmdirSync(path.join(config.dbPath, user._id), {recursive: true}); + return user._id; + }); + } + + /** + * Updates a user. + * + * @param {string} username - Name of the user to be updated. + * @param {Partial} userRecordPatch - Changes to the user. + * @return {Promise} - Returns new username of updated user or rejects with error. + */ + async updateUser(username: string, userRecordPatch: Partial): Promise { + const patch = userRecordPatch; + + if (patch.password != null) { + patch.password = await hashPassword(patch.password); + } + + return this.db.update({username}, {$set: patch}, {}).then((numUsersUpdated) => { + if (numUsersUpdated === 0) { + throw new Error(); + } + + return userRecordPatch.username || username; + }); + } + + /** + * Looks up a user. + * + * @param {string} username - Name of the user to be updated. + * @return {Promise} - Returns a user or rejects with error. + */ + async lookupUser(username: string): Promise { + if (config.authMethod === 'none') { + return this.getConfigUser(); + } + + return this.db.findOne({username}); + } + + /** + * Lists users. + * + * @return {Promise} - Returns users or rejects with error. + */ + async listUsers(): Promise { + if (config.authMethod === 'none') { + return [this.getConfigUser()]; + } + + return this.db.find({}); + } + + /** + * Gets the number of users and route to appropriate handler. + */ + async initialUserGate(handlers: {handleInitialUser: () => void; handleSubsequentUser: () => void}): Promise { + const userCount = await this.db.count({}); + + if (userCount && userCount > 0) { + return handlers.handleSubsequentUser(); + } + + return handlers.handleInitialUser(); + } +} + +export default new Users(); diff --git a/server/models/client.js b/server/models/client.js deleted file mode 100644 index 707b3b0c5..000000000 --- a/server/models/client.js +++ /dev/null @@ -1,380 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const series = require('run-series'); -const tar = require('tar-stream'); - -const ClientRequest = require('./ClientRequest'); -const clientResponseUtil = require('../util/clientResponseUtil'); -const clientSettingsMap = require('../../shared/constants/clientSettingsMap'); -const settings = require('./settings'); -const torrentFilePropsMap = require('../../shared/constants/torrentFilePropsMap'); -const torrentPeerPropsMap = require('../../shared/constants/torrentPeerPropsMap'); -const torrentStatusMap = require('../../shared/constants/torrentStatusMap'); -const torrentTrackerPropsMap = require('../../shared/constants/torrentTrackerPropsMap'); - -const client = { - addFiles(user, services, req, callback) { - const {files} = req; - const {destination: destinationPath, isBasePath, start} = req.body; - let {tags} = req.body; - const request = new ClientRequest(user, services); - - if (!Array.isArray(tags)) { - tags = tags.split(','); - } - - request.createDirectory({path: destinationPath}); - request.send(); - - // Each torrent is sent individually because rTorrent accepts a total - // filesize of 524 kilobytes or less. This allows the user to send many - // torrent files reliably. - files.forEach((file, index) => { - file.originalname = encodeURIComponent(file.originalname); - - const fileRequest = new ClientRequest(user, services); - fileRequest.addFiles({ - files: file, - path: destinationPath, - isBasePath, - start, - tags, - }); - - // Set the callback for only the last request. - if (index === files.length - 1) { - fileRequest.onComplete((response, error) => { - services.torrentService.fetchTorrentList(); - callback(response, error); - }); - } - - fileRequest.send(); - }); - - settings.set(user, {id: 'startTorrentsOnLoad', data: start === 'true' || start === true}); - }, - - addUrls(user, services, data, callback) { - const {urls, destination, isBasePath, start, tags} = data; - const request = new ClientRequest(user, services); - request.createDirectory({path: destination}); - request.addURLs({ - urls, - path: destination, - isBasePath, - start, - tags, - }); - request.onComplete(callback); - request.send(); - - settings.set(user, {id: 'startTorrentsOnLoad', data: start}); - }, - - checkHash(user, services, hashes, callback) { - const request = new ClientRequest(user, services); - - request.checkHash({hashes}); - request.onComplete((response, error) => { - services.torrentService.fetchTorrentList(); - callback(response, error); - }); - request.send(); - }, - - downloadFiles(user, services, hash, fileString, res) { - try { - const selectedTorrent = services.torrentService.getTorrent(hash); - if (!selectedTorrent) return res.status(404).json({error: 'Torrent not found.'}); - - this.getTorrentDetails(user, services, hash, torrentDetails => { - if (!torrentDetails) return res.status(404).json({error: 'Torrent details not found'}); - - let files; - if (!fileString) { - files = torrentDetails.fileTree.files.map((x, i) => `${i}`); - } else { - files = fileString.split(','); - } - - const filePathsToDownload = this.findFilesByIndicies(files, torrentDetails.fileTree).map(file => - path.join(selectedTorrent.directory, file.path), - ); - - if (filePathsToDownload.length === 1) { - const file = filePathsToDownload[0]; - if (!fs.existsSync(file)) return res.status(404).json({error: 'File not found.'}); - - res.attachment(path.basename(file)); - return res.download(file); - } - - res.attachment(`${selectedTorrent.name}.tar`); - - const pack = tar.pack(); - pack.pipe(res); - - const tasks = filePathsToDownload.map(filePath => { - const filename = path.basename(filePath); - - return next => { - fs.stat(filePath, (err, stats) => { - if (err) return next(err); - - const stream = fs.createReadStream(filePath); - const entry = pack.entry( - { - name: filename, - size: stats.size, - }, - next, - ); - stream.pipe(entry); - }); - }; - }); - - series(tasks, error => { - if (error) return res.status(500).json(error); - - pack.finalize(); - }); - }); - } catch (error) { - res.status(500).json(error); - } - }, - - findFilesByIndicies(indices, fileTree = {}) { - const {directories, files = []} = fileTree; - - let selectedFiles = files.filter(file => indices.includes(`${file.index}`)); - - if (directories != null) { - selectedFiles = selectedFiles.concat( - Object.keys(directories).reduce( - (accumulator, directory) => accumulator.concat(this.findFilesByIndicies(indices, directories[directory])), - [], - ), - ); - } - - return selectedFiles; - }, - - getSettings(user, services, options, callback) { - let requestedSettingsKeys = []; - const request = new ClientRequest(user, services); - const response = {}; - - const outboundTransformation = { - throttleGlobalDownMax: apiResponse => Number(apiResponse) / 1024, - throttleGlobalUpMax: apiResponse => Number(apiResponse) / 1024, - piecesMemoryMax: apiResponse => Number(apiResponse) / (1024 * 1024), - }; - - request.fetchSettings({ - options, - setRequestedKeysArr: requestedSettingsKeysArr => { - requestedSettingsKeys = requestedSettingsKeysArr; - }, - }); - - request.postProcess(data => { - if (!data) { - return null; - } - - data.forEach((datum, index) => { - let value = datum[0]; - const settingsKey = clientSettingsMap[requestedSettingsKeys[index]]; - - if (outboundTransformation[settingsKey]) { - value = outboundTransformation[settingsKey](value); - } - - response[settingsKey] = value; - }); - - return response; - }); - request.onComplete(callback); - request.send(); - }, - - getTorrentDetails(user, services, hash, callback) { - const request = new ClientRequest(user, services); - - request.getTorrentDetails({ - hash, - fileProps: torrentFilePropsMap.methods, - peerProps: torrentPeerPropsMap.methods, - trackerProps: torrentTrackerPropsMap.methods, - }); - request.postProcess(clientResponseUtil.processTorrentDetails); - request.onComplete(callback); - request.send(); - }, - - listMethods(user, services, method, args, callback) { - const request = new ClientRequest(user, services); - - request.listMethods({method, args}); - request.onComplete(callback); - request.send(); - }, - - moveTorrents(user, services, data, callback) { - const destinationPath = data.destination; - const {isBasePath, hashes, filenames, moveFiles, sourcePaths} = data; - const mainRequest = new ClientRequest(user, services); - - const hashesToRestart = hashes.filter( - hash => !services.torrentService.getTorrent(hash).status.includes(torrentStatusMap.stopped), - ); - - let afterCheckHash; - - if (hashesToRestart.length) { - afterCheckHash = () => { - const startTorrentsRequest = new ClientRequest(user, services); - startTorrentsRequest.startTorrents({hashes: hashesToRestart}); - startTorrentsRequest.onComplete(callback); - startTorrentsRequest.send(); - }; - } else { - afterCheckHash = callback; - } - - const checkHash = () => { - const checkHashRequest = new ClientRequest(user, services); - checkHashRequest.checkHash({hashes}); - checkHashRequest.onComplete(afterCheckHash); - checkHashRequest.send(); - }; - - const moveTorrents = () => { - const moveTorrentsRequest = new ClientRequest(user, services); - moveTorrentsRequest.onComplete(checkHash); - moveTorrentsRequest.moveTorrents({ - filenames, - sourcePaths, - destinationPath, - }); - }; - - let afterSetPath = checkHash; - - if (moveFiles) { - afterSetPath = moveTorrents; - } - - mainRequest.stopTorrents({hashes}); - mainRequest.setDownloadPath({hashes, path: destinationPath, isBasePath}); - mainRequest.onComplete(afterSetPath); - mainRequest.send(); - }, - - setFilePriority(user, services, hashes, data, callback) { - // TODO Add support for multiple hashes. - const {fileIndices} = data; - const request = new ClientRequest(user, services); - - request.setFilePriority({hashes, fileIndices, priority: data.priority}); - request.onComplete((response, error) => { - services.torrentService.fetchTorrentList(); - callback(response, error); - }); - request.send(); - }, - - setPriority(user, services, hashes, data, callback) { - const request = new ClientRequest(user, services); - - request.setPriority({hashes, priority: data.priority}); - request.onComplete((response, error) => { - services.torrentService.fetchTorrentList(); - callback(response, error); - }); - request.send(); - }, - - setSettings(user, services, payloads, callback) { - const request = new ClientRequest(user, services); - if (payloads.length === 0) return callback({}); - - const inboundTransformation = { - throttleGlobalDownMax: userInput => ({ - id: userInput.id, - data: Number(userInput.data) * 1024, - }), - throttleGlobalUpMax: userInput => ({ - id: userInput.id, - data: Number(userInput.data) * 1024, - }), - piecesMemoryMax: userInput => ({ - id: userInput.id, - data: (Number(userInput.data) * 1024 * 1024).toString(), - }), - }; - - const transformedPayloads = payloads.map(payload => { - if (inboundTransformation[payload.id]) { - return inboundTransformation[payload.id](payload); - } - - return payload; - }); - - request.setSettings({settings: transformedPayloads}); - request.onComplete(callback); - request.send(); - }, - - setSpeedLimits(user, services, data, callback) { - const request = new ClientRequest(user, services); - - request.setThrottle({ - direction: data.direction, - throttle: data.throttle, - }); - request.onComplete(callback); - request.send(); - }, - - setTaxonomy(user, services, data, callback) { - const request = new ClientRequest(user, services); - - request.setTaxonomy(data); - request.onComplete((response, error) => { - // Fetch the latest torrent list to re-index the taxonomy. - services.torrentService.fetchTorrentList(); - callback(response, error); - }); - request.send(); - }, - - stopTorrent(user, services, hashes, callback) { - const request = new ClientRequest(user, services); - request.stopTorrents({hashes}); - request.onComplete((response, error) => { - services.torrentService.fetchTorrentList(); - callback(response, error); - }); - request.send(); - }, - - startTorrent(user, services, hashes, callback) { - const request = new ClientRequest(user, services); - - request.startTorrents({hashes}); - request.onComplete((response, error) => { - services.torrentService.fetchTorrentList(); - callback(response, error); - }); - request.send(); - }, -}; - -module.exports = client; diff --git a/server/models/settings.js b/server/models/settings.js deleted file mode 100644 index c38e141cd..000000000 --- a/server/models/settings.js +++ /dev/null @@ -1,136 +0,0 @@ -const _ = require('lodash'); -const Datastore = require('nedb'); -const path = require('path'); - -const config = require('../../config'); - -const databases = new Map(); - -const changedKeys = { - downloadRate: 'downRate', - downloadTotal: 'downTotal', - uploadRate: 'upRate', - uploadTotal: 'upTotal', - connectedPeers: 'peersConnected', - totalPeers: 'peersTotal', - connectedSeeds: 'seedsConnected', - totalSeeds: 'seedsTotal', - added: 'dateAdded', - creationDate: 'dateCreated', - trackers: 'trackerURIs', -}; - -const removedKeys = ['freeDiskSpace']; - -/** - * Check settings for old torrent propery keys. If the old keys exist and have - * been assigned values, then check that the new key doesn't also exist. When - * the new key does not exist, add the new key and assign it the old key's - * value. - * - * @param {Object} settings - the stored settings object. - * @return {Object} - the settings object, altered if legacy keys exist. - */ -const transformLegacyKeys = settings => { - if (settings.sortTorrents && settings.sortTorrents.property in changedKeys) { - settings.sortTorrents.property = changedKeys[settings.sortTorrents.property]; - } - - if (settings.torrentDetails) { - settings.torrentDetails = settings.torrentDetails.reduce((accumulator, detailItem) => { - if ( - detailItem && - detailItem.id in changedKeys && - !settings.torrentDetails.some(subDetailItem => subDetailItem.id === changedKeys[detailItem.id]) - ) { - detailItem.id = changedKeys[detailItem.id]; - } - - if (!removedKeys.includes(detailItem.id)) { - accumulator.push(detailItem); - } - - return accumulator; - }, []); - } - - if (settings.torrentListColumnWidths) { - Object.keys(settings.torrentListColumnWidths).forEach(columnID => { - if (columnID in changedKeys && !(changedKeys[columnID] in settings.torrentListColumnWidths)) { - settings.torrentListColumnWidths[changedKeys[columnID]] = settings.torrentListColumnWidths[columnID]; - } - }); - } - - return settings; -}; - -function getDb(user) { - const userId = user._id; - - if (databases.has(userId)) { - return databases.get(userId); - } - - const database = new Datastore({ - autoload: true, - filename: path.join(config.dbPath, userId, 'settings', 'settings.db'), - }); - - databases.set(userId, database); - - return database; -} - -const settings = { - get: (user, opts, callback) => { - const query = {}; - const settingsToReturn = {}; - - if (opts.property) { - query.id = opts.property; - } - - getDb(user) - .find(query) - .exec((err, docs) => { - if (err) { - callback(null, err); - return; - } - - docs.forEach(doc => { - settingsToReturn[doc.id] = doc.data; - }); - - callback(transformLegacyKeys(settingsToReturn)); - }); - }, - - set: (user, payloads, callback = _.noop) => { - const docsResponse = []; - - if (!Array.isArray(payloads)) { - payloads = [payloads]; - } - - if (payloads && payloads.length) { - payloads.forEach((payload, index) => { - getDb(user).update({id: payload.id}, {$set: {data: payload.data}}, {upsert: true}, (err, docs) => { - docsResponse.push(docs); - if (index + 1 === payloads.length) { - if (err) { - callback(null, err); - return; - } - callback(docsResponse); - } - }); - }); - } else { - callback(); - } - }, -}; - -module.exports = settings; diff --git a/server/routes/api.js b/server/routes/api.js deleted file mode 100644 index 22de920d3..000000000 --- a/server/routes/api.js +++ /dev/null @@ -1,86 +0,0 @@ -const express = require('express'); -const passport = require('passport'); - -const router = express.Router(); - -const appendUserServices = require('../middleware/appendUserServices'); -const ajaxUtil = require('../util/ajaxUtil'); -const client = require('../models/client'); -const clientRoutes = require('./client'); -const clientActivityStream = require('../middleware/clientActivityStream'); -const eventStream = require('../middleware/eventStream'); -const Filesystem = require('../models/Filesystem'); -const mediainfo = require('../util/mediainfo'); -const settings = require('../models/settings'); - -router.use('/', passport.authenticate('jwt', {session: false}), appendUserServices); - -router.use('/client', clientRoutes); - -router.get('/activity-stream', eventStream, clientActivityStream); - -router.get('/download', (req, res) => { - client.downloadFiles(req.user, req.services, req.query.hash, req.query.files, res); -}); - -router.delete('/feed-monitor/:id', (req, res) => { - req.services.feedService.removeItem(req.params.id, ajaxUtil.getResponseFn(res)); -}); - -router.get('/feed-monitor', (req, res) => { - req.services.feedService.getAll(req.body.query, ajaxUtil.getResponseFn(res)); -}); - -router.get('/feed-monitor/feeds', (req, res) => { - req.services.feedService.getFeeds(req.body.query, ajaxUtil.getResponseFn(res)); -}); - -router.put('/feed-monitor/feeds', (req, res) => { - req.services.feedService.addFeed(req.body, ajaxUtil.getResponseFn(res)); -}); - -router.put('/feed-monitor/feeds/:id', (req, res) => { - req.services.feedService.modifyFeed(req.params.id, req.body, ajaxUtil.getResponseFn(res)); -}); - -router.get('/feed-monitor/rules', (req, res) => { - req.services.feedService.getRules(req.body.query, ajaxUtil.getResponseFn(res)); -}); - -router.put('/feed-monitor/rules', (req, res) => { - req.services.feedService.addRule(req.body, ajaxUtil.getResponseFn(res)); -}); - -router.get('/feed-monitor/items', (req, res) => { - req.services.feedService.getItems(req.query, ajaxUtil.getResponseFn(res)); -}); - -router.get('/directory-list', (req, res) => { - Filesystem.getDirectoryList(req.query, ajaxUtil.getResponseFn(res)); -}); - -router.get('/history', (req, res) => { - req.services.historyService.getHistory(req.query, ajaxUtil.getResponseFn(res)); -}); - -router.get('/mediainfo', (req, res) => { - mediainfo.getMediainfo(req.user, req.query, ajaxUtil.getResponseFn(res)); -}); - -router.get('/notifications', (req, res) => { - req.services.notificationService.getNotifications(req.query, ajaxUtil.getResponseFn(res)); -}); - -router.delete('/notifications', (req, res) => { - req.services.notificationService.clearNotifications(req.query, ajaxUtil.getResponseFn(res)); -}); - -router.get('/settings', (req, res) => { - settings.get(req.user, req.query, ajaxUtil.getResponseFn(res)); -}); - -router.patch('/settings', (req, res) => { - settings.set(req.user, req.body, ajaxUtil.getResponseFn(res)); -}); - -module.exports = router; diff --git a/server/routes/api/auth.test.ts b/server/routes/api/auth.test.ts new file mode 100644 index 000000000..3ed23c6b4 --- /dev/null +++ b/server/routes/api/auth.test.ts @@ -0,0 +1,491 @@ +import crypto from 'crypto'; +import supertest from 'supertest'; + +import {AccessLevel} from '../../../shared/schema/constants/Auth'; + +import app from '../../app'; +import {getAuthToken} from './auth'; + +import type { + AuthRegistrationOptions, + AuthUpdateUserOptions, + AuthVerificationResponse, +} from '../../../shared/schema/api/auth'; +import type {ClientConnectionSettings} from '../../../shared/schema/ClientConnectionSettings'; + +const request = supertest(app); + +const testConnectionSettings: ClientConnectionSettings = { + client: 'rTorrent', + type: 'socket', + version: 1, + socket: '/home/download/rtorrent.sock', +}; + +const testAdminUser = { + username: crypto.randomBytes(8).toString('hex'), + password: crypto.randomBytes(30).toString('hex'), + client: testConnectionSettings, + level: AccessLevel.ADMINISTRATOR, +} as const; +let testAdminUserToken = ''; + +const testNonAdminUser = { + username: crypto.randomBytes(8).toString('hex'), + password: crypto.randomBytes(30).toString('hex'), + client: testConnectionSettings, + level: AccessLevel.USER, +} as const; +let testNonAdminUserToken = ''; + +describe('GET /api/auth/verify (initial)', () => { + it('Verify without credential', (done) => { + request + .get('/api/auth/verify') + .send() + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + const verificationResponse: AuthVerificationResponse = res.body; + + expect(verificationResponse.initialUser).toBe(true); + expect(verificationResponse.configs).toBeDefined(); + + done(); + }); + }); +}); + +describe('POST /api/auth/register', () => { + it('Register initial user', (done) => { + const options: AuthRegistrationOptions = testAdminUser; + request + .post('/api/auth/register') + .send(options) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .expect('Set-Cookie', /jwt=.*;/) + .end((err, res) => { + if (err) done(err); + + [testAdminUserToken] = res.headers['set-cookie']; + expect(typeof testAdminUserToken).toBe('string'); + + done(); + }); + }); + + it('Register subsequent user with no credential', (done) => { + const options: AuthRegistrationOptions = testNonAdminUser; + request + .post('/api/auth/register') + .send(options) + .set('Accept', 'application/json') + .expect(401) + .end((err, res) => { + if (err) done(err); + + expect(res.headers['set-cookie']).toBeUndefined(); + + done(); + }); + }); + + it('Register subsequent user with admin credentials', (done) => { + const options: AuthRegistrationOptions = testNonAdminUser; + request + .post('/api/auth/register') + .send(options) + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(200) + .expect('Content-Type', /json/) + .expect('Set-Cookie', /jwt=.*;/) + .end((err, res) => { + if (err) done(err); + + [testNonAdminUserToken] = res.headers['set-cookie']; + expect(typeof testNonAdminUserToken).toBe('string'); + + done(); + }); + }); + + it('Register subsequent user with non-admin credentials', (done) => { + const options: AuthRegistrationOptions = testNonAdminUser; + request + .post('/api/auth/register') + .send(options) + .set('Accept', 'application/json') + .set('Cookie', [testNonAdminUserToken]) + .expect(403) + .end((err, res) => { + if (err) done(err); + + expect(res.headers['set-cookie']).toBeUndefined(); + + done(); + }); + }); + + it('Register duplicate user with admin credentials', (done) => { + const options: AuthRegistrationOptions = testNonAdminUser; + request + .post('/api/auth/register') + .send(options) + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(500) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.headers['set-cookie']).toBeUndefined(); + + done(); + }); + }); + + it('Register subsequent user with admin credentials expecting no cookie', (done) => { + const options: AuthRegistrationOptions = { + ...testNonAdminUser, + username: crypto.randomBytes(8).toString('hex'), + }; + request + .post('/api/auth/register?cookie=false') + .send(options) + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.headers['set-cookie']).toBeUndefined(); + + done(); + }); + }); + + it('Register subsequent user with admin credentials and malformed data', (done) => { + request + .post('/api/auth/register') + .send({ + ...testNonAdminUser, + client: { + ...testNonAdminUser.client, + client: 'not a client', + }, + }) + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(422) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.headers['set-cookie']).toBeUndefined(); + + done(); + }); + }); +}); + +describe('GET /api/auth/verify', () => { + it('Verify without credential', (done) => { + request + .get('/api/auth/verify') + .send() + .set('Accept', 'application/json') + .expect(401) + .end((err, res) => { + if (err) done(err); + + expect(res.body.configs).toBeDefined(); + + done(); + }); + }); + + it('Verify with valid credentials', (done) => { + request + .get('/api/auth/verify') + .send() + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(200) + .end((err, res) => { + if (err) done(err); + + const verificationResponse: AuthVerificationResponse = res.body; + + expect(verificationResponse.initialUser).toBe(false); + + if (verificationResponse.initialUser === false) { + expect(verificationResponse.level).toBe(testAdminUser.level); + expect(verificationResponse.username).toBe(testAdminUser.username); + } + + expect(verificationResponse.configs).toBeDefined(); + + done(); + }); + }); + + it('Verify with wrong credentials generated by server secret', (done) => { + request + .get('/api/auth/verify') + .send() + .set('Accept', 'application/json') + .set('Cookie', [`jwt=${getAuthToken('nonExistentUser')}`]) + .expect(401) + .end((err, res) => { + if (err) done(err); + + expect(res.body.configs).toBeDefined(); + + done(); + }); + }); +}); + +describe('GET /api/auth/logout', () => { + it('Logouts with credentials', (done) => { + request + .get('/api/auth/logout') + .send() + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(200) + .expect('Set-Cookie', /jwt=;/) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); + + it('Logouts without credential', (done) => { + request + .get('/api/auth/logout') + .send() + .set('Accept', 'application/json') + .expect(401) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); +}); + +describe('POST /api/auth/authenticate', () => { + it('Authenticate with no credential', (done) => { + request + .post('/api/auth/authenticate') + .send({ + username: 'root', + }) + .set('Accept', 'application/json') + .expect(422) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); + + it('Authenticate with wrong credentials', (done) => { + request + .post('/api/auth/authenticate') + .send({ + username: 'root', + password: 'admin', + }) + .set('Accept', 'application/json') + .expect(401) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); + + it('Authenticate with correct credentials', (done) => { + request + .post('/api/auth/authenticate') + .send({ + username: testAdminUser.username, + password: testAdminUser.password, + }) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .expect('Set-Cookie', /jwt/) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); +}); + +describe('GET /api/auth/users', () => { + it('Lists user without credential', (done) => { + request + .get('/api/auth/users') + .send() + .set('Accept', 'application/json') + .expect(401) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); + + it('Lists user with non-admin credentials', (done) => { + request + .get('/api/auth/users') + .send() + .set('Accept', 'application/json') + .set('Cookie', [testNonAdminUserToken]) + .expect(403) + .end((err, res) => { + if (err) done(err); + + expect(Array.isArray(res.body)).toBe(false); + + done(); + }); + }); + + it('Lists user with admin credentials', (done) => { + request + .get('/api/auth/users') + .send() + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(200) + .end((err, res) => { + if (err) done(err); + + expect(Array.isArray(res.body)).toBe(true); + expect(typeof res.body[0].username).toBe('string'); + + done(); + }); + }); +}); + +describe('PATCH /api/auth/users/{username}', () => { + const patch: AuthUpdateUserOptions = { + client: { + client: 'rTorrent', + type: 'socket', + version: 1, + socket: 'test', + }, + }; + + it('Updates a nonexistent user with admin credentials', (done) => { + request + .patch(`/api/auth/users/${`nonExistentUser`}`) + .send(patch) + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(500) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); + + it('Updates an existing user with non-admin credentials', (done) => { + request + .patch(`/api/auth/users/${testAdminUser.username}`) + .send(patch) + .set('Accept', 'application/json') + .set('Cookie', [testNonAdminUserToken]) + .expect(403) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); + + it('Updates an existing user with admin credentials', (done) => { + request + .patch(`/api/auth/users/${testNonAdminUser.username}`) + .send(patch) + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(200) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); + + it('Updates an existing user with admin credentials and malformed data', (done) => { + request + .patch(`/api/auth/users/${testNonAdminUser.username}`) + .send({ + client: { + ...patch.client, + client: 'notClient', + }, + }) + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(422) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); +}); + +describe('DELETE /api/auth/users/{username}', () => { + it('Deletes a nonexistent user with admin credentials', (done) => { + request + .delete(`/api/auth/users/${`nonExistentUser`}`) + .send() + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(500) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); + + it('Deletes an existing user with non-admin credentials', (done) => { + request + .delete(`/api/auth/users/${testAdminUser.username}`) + .send() + .set('Accept', 'application/json') + .set('Cookie', [testNonAdminUserToken]) + .expect(403) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); + + it('Deletes an existing user with admin credentials', (done) => { + request + .delete(`/api/auth/users/${testNonAdminUser.username}`) + .send() + .set('Accept', 'application/json') + .set('Cookie', [testAdminUserToken]) + .expect(200) + .end((err, res) => { + if (err) done(err); + + expect(res.body.username).toBe(testNonAdminUser.username); + + done(); + }); + }); +}); diff --git a/server/routes/api/auth.ts b/server/routes/api/auth.ts new file mode 100644 index 000000000..839375050 --- /dev/null +++ b/server/routes/api/auth.ts @@ -0,0 +1,373 @@ +import express from 'express'; +import jwt from 'jsonwebtoken'; +import passport from 'passport'; +import rateLimit from 'express-rate-limit'; + +import type {Response} from 'express'; + +import { + authAuthenticationSchema, + authRegistrationSchema, + authUpdateUserSchema, + AuthVerificationPreloadConfigs, +} from '../../../shared/schema/api/auth'; +import config from '../../../config'; +import {getResponseFn, validationError} from '../../util/ajaxUtil'; +import requireAdmin from '../../middleware/requireAdmin'; +import services from '../../services'; +import Users from '../../models/Users'; + +import type { + AuthAuthenticationOptions, + AuthAuthenticationResponse, + AuthRegistrationOptions, + AuthUpdateUserOptions, + AuthVerificationResponse, +} from '../../../shared/schema/api/auth'; +import type {Credentials, UserInDatabase} from '../../../shared/schema/Auth'; + +const router = express.Router(); + +const failedLoginResponse = 'Failed login.'; + +// Limit each IP to 200 request every 5 minutes +// to prevent brute forcing password or denial-of-service +router.use( + '/', + rateLimit({ + windowMs: 5 * 60 * 1000, + max: 200, + }), +); + +export const getAuthToken = (username: string, res?: Response): string => { + const expirationSeconds = 60 * 60 * 24 * 7; // one week + const cookieExpiration = Date.now() + expirationSeconds * 1000; + + // Create token if the password matched and no error was thrown. + const token = jwt.sign({username}, config.secret, { + expiresIn: expirationSeconds, + }); + + if (res != null) { + res.cookie('jwt', token, { + expires: new Date(cookieExpiration), + httpOnly: true, + sameSite: 'strict', + }); + } + + return token; +}; + +const sendAuthenticationResponse = ( + res: Response, + credentials: Required>, +): void => { + const {username, level} = credentials; + + getAuthToken(username, res); + + const response: AuthAuthenticationResponse = { + success: true, + username, + level, + }; + + res.json(response); +}; + +const preloadConfigs: AuthVerificationPreloadConfigs = { + authMethod: config.authMethod, + pollInterval: config.torrentClientPollInterval, +}; + +router.use('/users', passport.authenticate('jwt', {session: false}), requireAdmin); + +/** + * POST /api/auth/authenticate + * @summary Authenticates a user + * @tags Auth + * @security None + * @param {AuthAuthenticationOptions} request.body.required - options - application/json + * @return {object} 422 - request validation error - application/json + * @return {object} 401 - incorrect username or password - application/json + * @return {AuthAuthenticationResponse} 200 - success response - application/json + */ +router.post('/authenticate', (req, res) => { + if (config.authMethod === 'none') { + sendAuthenticationResponse(res, Users.getConfigUser()); + return; + } + + const parsedResult = authAuthenticationSchema.safeParse(req.body); + + if (!parsedResult.success) { + validationError(res, parsedResult.error); + return; + } + + const credentials = parsedResult.data; + + Users.comparePassword(credentials).then( + (level) => { + sendAuthenticationResponse(res, { + ...credentials, + level, + }); + }, + () => { + // Incorrect username or password. + res.status(401).json({ + message: failedLoginResponse, + }); + }, + ); +}); + +// Allow unauthenticated registration if no users are currently registered. +router.use('/register', (req, res, next) => { + Users.initialUserGate({ + handleInitialUser: () => { + next(); + }, + handleSubsequentUser: () => { + passport.authenticate('jwt', {session: false}, (err, user: UserInDatabase) => { + if (err || !user) { + res.status(401).send('Unauthorized'); + return; + } + req.user = user; + // Only admin users can create users + requireAdmin(req, res, next); + })(req, res, next); + }, + }); +}); + +/** + * POST /api/auth/register + * @summary Registers a user + * @tags Auth + * @security None - initial request + * @security Administrator - subsequent requests + * @param {AuthRegistrationOptions} request.body.required - options - application/json + * @param {'true' | 'false'} cookie.query - whether to Set-Cookie if succeeded + * @return {string} 404 - registration is disabled + * @return {string} 403 - user is not authorized to create user + * @return {object} 422 - request validation error - application/json + * @return {{username: string}} 200 - success response if cookie=false - application/json + * @return {AuthAuthenticationResponse} 200 - success response - application/json + */ +router.post('/register', (req, res) => { + // No user can be registered when authMethod is none + if (config.authMethod === 'none') { + // Return 404 + res.status(404).send('Not found'); + return; + } + + const parsedResult = authRegistrationSchema.safeParse(req.body); + + if (!parsedResult.success) { + validationError(res, parsedResult.error); + return; + } + + const credentials = parsedResult.data; + + // Attempt to save the user + Users.createUser(credentials).then( + (user) => { + services.bootstrapServicesForUser(user); + + if (req.query.cookie === 'false') { + getResponseFn(res)({username: user.username}); + return; + } + + sendAuthenticationResponse(res, credentials); + }, + (err) => { + getResponseFn(res)({username: credentials.username}, err); + }, + ); +}); + +// Allow unauthenticated verification if no users are currently registered. +router.use('/verify', (req, res, next) => { + // Unconditionally provide a token if auth is disabled + if (config.authMethod === 'none') { + const {username, level} = Users.getConfigUser(); + + getAuthToken(username, res); + + const response: AuthVerificationResponse = { + initialUser: false, + username, + level, + configs: preloadConfigs, + }; + + res.json(response); + return; + } + + Users.initialUserGate({ + handleInitialUser: () => { + const response: AuthVerificationResponse = { + initialUser: true, + configs: preloadConfigs, + }; + res.json(response); + }, + handleSubsequentUser: () => { + passport.authenticate('jwt', {session: false}, (err, user: UserInDatabase) => { + if (err || !user) { + res.status(401).json({ + configs: preloadConfigs, + }); + return; + } + + req.user = user; + next(); + })(req, res, next); + }, + }); +}); + +/** + * GET /api/auth/verify + * @summary Verifies the connectivity and validity of session + * @tags Auth + * @security User + * @return {string} 401 - not authenticated or token expired + * @return {string} 500 - authenticated succeeded but user is unattached (this should NOT happen) + * @return {AuthVerificationResponse} 200 - success response - application/json + */ +router.get('/verify', (req, res) => { + if (req.user == null) { + res.status(500).send('Unattached user.'); + return; + } + + const response: AuthVerificationResponse = { + initialUser: false, + username: req.user.username, + level: req.user.level, + configs: preloadConfigs, + }; + + res.json(response); +}); + +// All subsequent routes are protected. +router.use('/', passport.authenticate('jwt', {session: false})); + +/** + * GET /api/auth/logout + * @summary Clears the session cookie + * @tags Auth + * @security User + * @return {string} 401 - not authenticated or token expired + * @return {} 200 - success response + */ +router.get('/logout', (_req, res) => { + res.clearCookie('jwt').send(); +}); + +// All subsequent routes need administrator access. +router.use('/', requireAdmin); + +router.use('/users', (_req, res, next) => { + // No operation on user when authMethod is none + if (config.authMethod === 'none') { + // Return 404 + res.status(404).send('Not found'); + } + + next(); +}); + +/** + * GET /api/auth/users + * @summary Lists all users + * @tags Auth + * @security Administrator + * @return {string} 401 - not authenticated or token expired + * @return {string} 403 - user is not authorized to list users + * @return {Array>} 200 - success response - application/json + */ +router.get('/users', (_req, res) => { + Users.listUsers().then( + (users) => + res.json( + users.map((user) => ({ + username: user.username, + level: user.level, + })), + ), + () => res.status(500).json(new Error()), + ); +}); + +/** + * DELETE /api/auth/users/{username} + * @summary Deletes a user + * @tags Auth + * @security Administrator + * @param {string} username.path - username of the user to be deleted + * @return {string} 401 - not authenticated or token expired + * @return {string} 403 - user is not authorized to delete user + * @return {{username: string}} 200 - success response - application/json + */ +router.delete('/users/:username', (req, res) => { + Users.removeUser(req.params.username) + .then((id) => { + services.destroyUserServices(id); + res.json({username: req.params.username}); + }) + .catch((err) => { + res.status(500).json(err); + }); +}); + +/** + * PATCH /api/auth/users/{username} + * @summary Updates a user + * @tags Auth + * @security Administrator + * @param {string} username.path - username of the user to be updated + * @param {AuthUpdateUserOptions} request.body.required - options - application/json + * @return {string} 401 - not authenticated or token expired + * @return {string} 403 - user is not authorized to update user + * @return {object} 422 - request validation error - application/json + * @return {} 200 - success response + */ +router.patch<{username: Credentials['username']}, unknown, AuthUpdateUserOptions>('/users/:username', (req, res) => { + const {username} = req.params; + + const parsedResult = authUpdateUserSchema.safeParse(req.body); + + if (!parsedResult.success) { + validationError(res, parsedResult.error); + return; + } + + const patch = parsedResult.data; + + Users.updateUser(username, patch) + .then((newUsername) => { + return Users.lookupUser(newUsername).then((user) => { + services.destroyUserServices(user._id); + services.bootstrapServicesForUser(user); + res.send(); + }); + }) + .catch((err) => { + res.status(500).json(err); + }); +}); + +export default router; diff --git a/server/routes/api/client.test.ts b/server/routes/api/client.test.ts new file mode 100644 index 000000000..df9663884 --- /dev/null +++ b/server/routes/api/client.test.ts @@ -0,0 +1,71 @@ +import supertest from 'supertest'; + +import app from '../../app'; +import {getAuthToken} from './auth'; + +import type {ClientSettings} from '../../../shared/types/ClientSettings'; + +const request = supertest(app); + +const authToken = `jwt=${getAuthToken('_config')}`; + +jest.setTimeout(20000); + +describe('GET /api/client/connection-test', () => { + it('Checks connection status', (done) => { + request + .get('/api/client/connection-test') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.body).toMatchObject({isConnected: true}); + + done(); + }); + }); +}); + +const settings: Partial = { + throttleGlobalDownSpeed: 2048 * 1024, + throttleGlobalUpSpeed: 2048 * 1024, +}; + +describe('PATCH /api/client/settings', () => { + it('Sets client settings', (done) => { + request + .patch('/api/client/settings') + .send(settings) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + done(); + }); + }); +}); + +describe('GET /api/client/settings', () => { + it('Gets all client settings', (done) => { + request + .get('/api/client/settings') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.body).toMatchObject(settings); + + done(); + }); + }); +}); diff --git a/server/routes/api/client.ts b/server/routes/api/client.ts new file mode 100644 index 000000000..85fa4bb97 --- /dev/null +++ b/server/routes/api/client.ts @@ -0,0 +1,82 @@ +import express from 'express'; + +import type {ClientSettings} from '@shared/types/ClientSettings'; +import type {SetClientSettingsOptions} from '@shared/types/api/client'; + +import {getResponseFn} from '../../util/ajaxUtil'; +import requireAdmin from '../../middleware/requireAdmin'; + +// Those settings don't require administrator access. +const SAFE_CLIENT_SETTINGS: Array = ['throttleGlobalDownSpeed', 'throttleGlobalUpSpeed']; + +const router = express.Router(); + +/** + * GET /api/client/connection-test + * @summary Tests connection to the torrent client + * @tags Client + * @security User + * @return {{isConnected: true}} 200 - success response - application/json + * @return {{isConnected: false}} 500 - failure response - application/json + */ +router.get('/connection-test', (req, res) => { + req.services?.clientGatewayService + ?.testGateway() + .then(() => { + res.status(200).json({isConnected: true}); + }) + .catch(() => { + res.status(500).json({isConnected: false}); + }); +}); + +/** + * GET /api/client/settings + * @summary Gets settings of torrent client managed by Flood. + * @tags Client + * @security User + * @return {ClientSettings} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get('/settings', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.getClientSettings() + .then(callback) + .catch((e) => callback(null, e)); +}); + +/** + * PATCH /api/client/settings + * @summary Sets settings of torrent client managed by Flood. + * @tags Client + * @security User - safe settings + * @security Administrator - sensitive settings + * @param {SetClientSettingsOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.patch('/settings', (req, res, next) => { + if ( + Object.keys(req.body).some((key) => { + return !SAFE_CLIENT_SETTINGS.includes(key as keyof ClientSettings); + }) + ) { + // Some settings are sensitive (e.g. can open undesired ports on the instance or make the + // instance send unsanctioned requests to another machine). So administrator access is required. + requireAdmin(req, res, next); + } else { + next(); + } +}); +router.patch('/settings', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.setClientSettings(req.body) + .then(callback) + .catch((e) => callback(null, e)); +}); + +export default router; diff --git a/server/routes/api/feed-monitor.test.ts b/server/routes/api/feed-monitor.test.ts new file mode 100644 index 000000000..0dd10ff07 --- /dev/null +++ b/server/routes/api/feed-monitor.test.ts @@ -0,0 +1,334 @@ +import fs from 'fs'; +import supertest from 'supertest'; + +import app from '../../app'; +import {getAuthToken} from './auth'; +import {getTempPath} from '../../models/TemporaryStorage'; + +import type {AddFeedOptions, AddRuleOptions, ModifyFeedOptions} from '../../../shared/types/api/feed-monitor'; +import type {Feed, Rule} from '../../../shared/types/Feed'; + +const request = supertest(app); + +const authToken = `jwt=${getAuthToken('_config')}`; + +const feed: AddFeedOptions = { + label: 'NYTimes', + url: 'https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml', + interval: 1, +}; + +let addedFeed: Feed | null = null; + +describe('GET /api/feed-monitor', () => { + it('Expects nothing, yet. Verifies data structure.', (done) => { + request + .get('/api/feed-monitor') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + const expectedResponse = { + feeds: [], + rules: [], + }; + + expect(res.body).toStrictEqual(expectedResponse); + + done(); + }); + }); +}); + +describe('PUT /api/feed-monitor/feeds', () => { + it('Subscribes to a feed', (done) => { + request + .put('/api/feed-monitor/feeds') + .send(feed) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + const response: Feed = res.body; + + expect(response).toMatchObject(feed); + + expect(response._id).not.toBeNull(); + expect(typeof response._id).toBe('string'); + + addedFeed = response; + + done(); + }); + }); + + it('GET /api/feed-monitor to verify added feed', (done) => { + request + .get('/api/feed-monitor') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + const expectedResponse = { + feeds: [addedFeed], + rules: [], + }; + + expect(res.body).toStrictEqual(expectedResponse); + + done(); + }); + }); + + it('GET /api/feed-monitor/feeds to verify added feed', (done) => { + request + .get('/api/feed-monitor/feeds') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.body).toStrictEqual([addedFeed]); + + done(); + }); + }); +}); + +describe('PATCH /api/feed-monitor/feeds/{id}', () => { + const modifyFeedOptions: ModifyFeedOptions = { + label: 'Modified Feed', + }; + + it('Modifies the added feed', (done) => { + expect(addedFeed).not.toBe(null); + if (addedFeed == null) return; + + request + .patch(`/api/feed-monitor/feeds/${addedFeed._id}`) + .send(modifyFeedOptions) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + + done(); + }); + }); + + it('GET /api/feed-monitor/feeds/{id} to verify modified feed', (done) => { + expect(addedFeed).not.toBe(null); + if (addedFeed == null) return; + + request + .get(`/api/feed-monitor/feeds/${addedFeed._id}`) + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + addedFeed = {...(addedFeed as Feed), ...modifyFeedOptions}; + + expect(res.body).toStrictEqual([addedFeed]); + + done(); + }); + }); +}); + +describe('GET /api/feed-monitor/feeds/{id}/items', () => { + it('Requests items of the feed', (done) => { + expect(addedFeed).not.toBe(null); + if (addedFeed == null) return; + + request + .get(`/api/feed-monitor/feeds/${addedFeed._id}/items`) + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(Array.isArray(res.body)).toBe(true); + + done(); + }); + }); +}); + +const tempDirectory = getTempPath('download'); + +fs.mkdirSync(tempDirectory, {recursive: true}); + +let addedRule: Rule; + +describe('GET /api/feed-monitor/rules', () => { + it('Expects nothing, verifies the response is an array', (done) => { + request + .get(`/api/feed-monitor/rules`) + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(Array.isArray(res.body)).toBe(true); + + done(); + }); + }); +}); + +describe('PUT /api/feed-monitor/rules', () => { + const rule: AddRuleOptions = { + label: 'Test rule', + feedIDs: [''], + match: '', + exclude: '.*', + destination: tempDirectory, + tags: ['FeedItem'], + startOnLoad: false, + }; + + it('Adds an automation rule', (done) => { + expect(addedFeed).not.toBe(null); + if (addedFeed == null) return; + rule.feedIDs = [addedFeed._id]; + + request + .put('/api/feed-monitor/rules') + .send(rule) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + const response: Rule = res.body; + + expect(response).toMatchObject(rule); + + expect(response._id).not.toBeNull(); + expect(typeof response._id).toBe('string'); + + addedRule = response; + + done(); + }); + }); + + it('GET /api/feed-monitor to verify added rule', (done) => { + request + .get('/api/feed-monitor') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.body.rules).toStrictEqual([addedRule]); + + done(); + }); + }); + + it('GET /api/feed-monitor/rules to verify added rule', (done) => { + request + .get('/api/feed-monitor/rules') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.body).toStrictEqual([addedRule]); + + done(); + }); + }); +}); + +describe('DELETE /api/feed-monitor/{id}', () => { + it('Deletes the added feed', (done) => { + expect(addedFeed).not.toBe(null); + if (addedFeed == null) return; + + request + .delete(`/api/feed-monitor/${addedFeed._id}`) + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + + done(); + }); + }); + + it('Deletes the added rule', (done) => { + request + .delete(`/api/feed-monitor/${addedRule._id}`) + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + + done(); + }); + }); + + it('GET /api/feed-monitor to verify feed and rule are deleted', (done) => { + request + .get('/api/feed-monitor') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + const expectedResponse = { + feeds: [], + rules: [], + }; + + expect(res.body).toStrictEqual(expectedResponse); + + done(); + }); + }); +}); diff --git a/server/routes/api/feed-monitor.ts b/server/routes/api/feed-monitor.ts new file mode 100644 index 000000000..0156d6538 --- /dev/null +++ b/server/routes/api/feed-monitor.ts @@ -0,0 +1,198 @@ +import express from 'express'; + +import type {AddFeedOptions, AddRuleOptions, ModifyFeedOptions} from '@shared/types/api/feed-monitor'; + +import {accessDeniedError, isAllowedPath, sanitizePath} from '../../util/fileUtil'; +import {getResponseFn} from '../../util/ajaxUtil'; + +const router = express.Router(); + +/** + * GET /api/feed-monitor + * @summary Gets subscribed feeds and their automation rules + * @tags Feeds + * @security User + * @return {{feeds: Array; rules: Array}} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get('/', (req, res) => { + const callback = getResponseFn(res); + + req.services?.feedService + .getAll() + .then((feedsAndRules) => { + callback(feedsAndRules); + }) + .catch((error) => { + callback(null, error); + }); +}); + +/** + * DELETE /api/feed-monitor/{id} + * @summary Deletes feed subscription or automation rule + * @tags Feeds + * @security User + * @param id.path - Unique ID of the item + * @return {} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.delete<{id: string}>('/:id', (req, res) => { + const callback = getResponseFn(res); + + req.services?.feedService + .removeItem(req.params.id) + .then(() => { + callback(null); + }) + .catch((error) => { + callback(null, error); + }); +}); + +/** + * GET /api/feed-monitor/feeds/{id?} + * @summary Gets subscribed feeds + * @tags Feeds + * @security User + * @param id.path.optional - Unique ID of the feed subscription + * @return {Array}} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get<{id?: string}>('/feeds/:id?', (req, res) => { + const callback = getResponseFn(res); + + req.services?.feedService + .getFeeds(req.params.id) + .then((feeds) => { + callback(feeds); + }) + .catch((error) => { + callback(null, error); + }); +}); + +/** + * PUT /api/feed-monitor/feeds + * @summary Subscribes to a feed + * @tags Feeds + * @security User + * @param {AddFeedOptions} request.body.required - options - application/json + * @return {Feed} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.put('/feeds', (req, res) => { + const callback = getResponseFn(res); + + req.services?.feedService + .addFeed(req.body) + .then((feed) => { + callback(feed); + }) + .catch((error) => { + callback(null, error); + }); +}); + +/** + * PATCH /api/feed-monitor/feeds/{id} + * @summary Modifies the options of a feed subscription + * @tags Feeds + * @security User + * @param id.path - Unique ID of the feed subscription + * @param {ModifyFeedOptions} request.body.required - options - application/json + * @return {} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.patch<{id: string}, unknown, ModifyFeedOptions>('/feeds/:id', (req, res) => { + const callback = getResponseFn(res); + + req.services?.feedService + .modifyFeed(req.params.id, req.body) + .then(() => { + callback(null); + }) + .catch((error) => { + callback(null, error); + }); +}); + +/** + * GET /api/feed-monitor/feeds/{id}/items?search= + * @summary Gets items in a feed + * @tags Feeds + * @security User + * @param id.path - Unique ID of the feed subscription + * @param {string} search.query - string to search in items + * @return {Array} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get<{id: string}, unknown, ModifyFeedOptions, {search: string}>('/feeds/:id/items', (req, res) => { + const callback = getResponseFn(res); + + req.services?.feedService + .getItems(req.params.id, req.query.search) + .then((items) => { + callback(items); + }) + .catch((error) => { + callback(null, error); + }); +}); + +/** + * GET /api/feed-monitor/rules + * @summary Gets automation rules + * @tags Feeds + * @security User + * @return {Array}} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get('/rules', (req, res) => { + const callback = getResponseFn(res); + + req.services?.feedService + .getRules() + .then((rules) => { + callback(rules); + }) + .catch((error) => { + callback(null, error); + }); +}); + +/** + * PUT /api/feed-monitor/rules + * @summary Adds an automation rule to a feed subscription + * @tags Feeds + * @security User + * @param {AddRuleOptions} request.body.required - options - application/json + * @return {Rule} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.put('/rules', (req, res) => { + const callback = getResponseFn(res); + + let sanitizedPath: string | null = null; + try { + sanitizedPath = sanitizePath(req.body.destination); + if (!isAllowedPath(sanitizedPath)) { + callback(null, accessDeniedError()); + return; + } + } catch (e) { + callback(null, e); + return; + } + + req.services?.feedService + .addRule({...req.body, destination: sanitizedPath}) + .then((rule) => { + callback(rule); + }) + .catch((error) => { + callback(null, error); + }); +}); + +export default router; diff --git a/server/routes/api/index.test.ts b/server/routes/api/index.test.ts new file mode 100644 index 000000000..b4d990e28 --- /dev/null +++ b/server/routes/api/index.test.ts @@ -0,0 +1,76 @@ +import supertest from 'supertest'; + +import app from '../../app'; +import {getAuthToken} from './auth'; + +import type {FloodSettings} from '../../../shared/types/FloodSettings'; + +const request = supertest(app); + +const authToken = `jwt=${getAuthToken('_config')}`; + +const settings: Partial = { + startTorrentsOnLoad: false, + torrentDestinations: { + '': '/home/download/test', + }, +}; + +describe('PATCH /api/settings', () => { + it('Adds settings', (done) => { + request + .patch('/api/settings') + .send(settings) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.body).toStrictEqual(settings); + + done(); + }); + }); +}); + +describe('GET /api/settings', () => { + it('Gets all settings', (done) => { + request + .get('/api/settings') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.body).toMatchObject(settings); + + done(); + }); + }); +}); + +describe('GET /api/settings/{property}', () => { + Object.keys(settings).forEach((setting) => { + it(`Gets property ${setting}`, (done) => { + request + .get(`/api/settings/${setting}`) + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(settings).toMatchObject(res.body); + + done(); + }); + }); + }); +}); diff --git a/server/routes/api/index.ts b/server/routes/api/index.ts new file mode 100644 index 000000000..8aeece399 --- /dev/null +++ b/server/routes/api/index.ts @@ -0,0 +1,146 @@ +import express from 'express'; +import passport from 'passport'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; +import type {HistorySnapshot} from '@shared/constants/historySnapshotTypes'; +import type {NotificationFetchOptions} from '@shared/types/Notification'; +import type {SetFloodSettingsOptions} from '@shared/types/api/index'; + +import appendUserServices from '../../middleware/appendUserServices'; +import authRoutes from './auth'; +import clientRoutes from './client'; +import clientActivityStream from '../../middleware/clientActivityStream'; +import eventStream from '../../middleware/eventStream'; +import feedMonitorRoutes from './feed-monitor'; +import {getDirectoryList} from '../../util/fileUtil'; +import {getResponseFn} from '../../util/ajaxUtil'; +import torrentsRoutes from './torrents'; + +const router = express.Router(); + +router.use('/auth', authRoutes); + +// All subsequent routes need authentication +router.use('/', passport.authenticate('jwt', {session: false}), appendUserServices); + +router.use('/client', clientRoutes); + +router.use('/feed-monitor', feedMonitorRoutes); + +router.use('/torrents', torrentsRoutes); + +/** + * GET /api/activity-stream + * @summary Subscribes to activity stream + * @tags Flood + * @security User + * @return {EventSource} 200 - success response - text/event-stream + * @return {Error} 500 - failure response - application/json + */ +router.get('/activity-stream', eventStream, clientActivityStream); + +router.get('/directory-list', (req, res) => { + const callback = getResponseFn(res); + getDirectoryList(req.query.path) + .then((data) => { + callback(data); + }) + .catch((error) => { + callback(null, error); + }); +}); + +/** + * GET /api/history + * @summary Gets transfer history in the given interval + * @tags Flood + * @security User + * @param {HistorySnapshot} snapshot.query - interval + * @return {TransferHistory} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get('/history', (req, res) => { + req.services?.historyService.getHistory(req.query).then( + (snapshot) => { + res.json(snapshot); + }, + (err) => { + res.status(500).json(err); + }, + ); +}); + +router.get('/notifications', (req, res) => { + req.services?.notificationService.getNotifications(req.query, getResponseFn(res)); +}); + +router.delete('/notifications', (req, res) => { + req.services?.notificationService.clearNotifications(getResponseFn(res)); +}); + +/** + * GET /api/settings + * @summary Gets all Flood's settings + * @tags Flood + * @security User + * @return {FloodSettings} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get('/settings', (req, res) => { + const callback = getResponseFn(res); + + req.services?.settingService + .get(null) + .then((settings) => { + callback(settings as FloodSettings); + }) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * GET /api/settings/{property} + * @summary Gets Flood's settings + * @tags Flood + * @security User + * @param property.path + * @return {Partial} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get<{property: keyof FloodSettings}>('/settings/:property', (req, res) => { + const callback = getResponseFn(res); + + req.services?.settingService + .get(req.params.property) + .then((settings) => { + callback(settings); + }) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * PATCH /api/settings + * @summary Sets Flood's settings + * @tags Flood + * @security User + * @param {Partial} request.body.required - options - application/json + * @return {Partial} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.patch('/settings', (req, res) => { + const callback = getResponseFn(res); + + req.services?.settingService + .set(req.body) + .then((savedSettings) => { + callback(savedSettings); + }) + .catch((err) => { + callback(null, err); + }); +}); + +export default router; diff --git a/server/routes/api/torrents.test.ts b/server/routes/api/torrents.test.ts new file mode 100644 index 000000000..09fbd47e4 --- /dev/null +++ b/server/routes/api/torrents.test.ts @@ -0,0 +1,501 @@ +import crypto from 'crypto'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import readline from 'readline'; +import stream from 'stream'; +import supertest from 'supertest'; + +import app from '../../app'; +import {getAuthToken} from './auth'; +import {getTempPath} from '../../models/TemporaryStorage'; +import paths from '../../../shared/config/paths'; + +import type {AddTorrentByFileOptions, AddTorrentByURLOptions} from '../../../shared/schema/api/torrents'; +import type { + CreateTorrentOptions, + MoveTorrentsOptions, + SetTorrentsTrackersOptions, +} from '../../../shared/types/api/torrents'; +import type {TorrentContent} from '../../../shared/types/TorrentContent'; +import type {TorrentList} from '../../../shared/types/Torrent'; +import type {TorrentStatus} from '../../../shared/constants/torrentStatusMap'; +import type {TorrentTracker} from '../../../shared/types/TorrentTracker'; + +const request = supertest(app); + +const authToken = `jwt=${getAuthToken('_config')}`; + +const tempDirectory = getTempPath('download'); + +jest.setTimeout(20000); + +const torrentFiles = [ + path.join(paths.appSrc, 'fixtures/single.torrent'), + path.join(paths.appSrc, 'fixtures/multi.torrent'), +].map((torrentPath) => Buffer.from(fs.readFileSync(torrentPath)).toString('base64')) as [string, ...string[]]; + +const torrentURLs: [string, ...string[]] = [ + 'https://releases.ubuntu.com/20.04/ubuntu-20.04.1-live-server-amd64.iso.torrent', + 'https://flood.js.org/api/test-cookie', +]; + +const torrentCookies = { + 'flood.js.org': ['test=test'], +}; + +const testTrackers = [ + `https://${crypto.randomBytes(8).toString('hex')}.com/announce`, + `http://${crypto.randomBytes(8).toString('hex')}.com/announce?key=test`, + `http://${crypto.randomBytes(8).toString('hex')}.com/announce.php?key=test`, +]; + +let torrentHash = ''; +let createdTorrentHash = ''; + +const activityStream = new stream.PassThrough(); +const rl = readline.createInterface({input: activityStream}); +request.get('/api/activity-stream').send().set('Cookie', [authToken]).pipe(activityStream); + +const watchTorrentList = (op: 'add' | 'remove' | 'replace' | 'move' | 'copy' | 'test'): Promise => { + return new Promise((resolve) => { + let eventDetected = false; + rl.on('line', (input) => { + if (eventDetected && input.includes(`"op":"${op}"`)) { + resolve(); + } + if (input.includes('TORRENT_LIST_DIFF_CHANGE')) { + eventDetected = true; + } + }); + }); +}; + +describe('POST /api/torrents/add-urls', () => { + const addTorrentByURLOptions: AddTorrentByURLOptions = { + urls: torrentURLs, + cookies: torrentCookies, + destination: tempDirectory, + tags: ['testURLs'], + isBasePath: false, + start: false, + }; + + it('Adds torrents via URLs with incorrect options', (done) => { + request + .post('/api/torrents/add-urls') + .send({...addTorrentByURLOptions, nonExistingOption: 1}) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(422) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + + done(); + }); + }); + + it('Adds torrents to disallowed path via URLs', (done) => { + request + .post('/api/torrents/add-urls') + .send({ + ...addTorrentByURLOptions, + destination: path.join(os.tmpdir(), 'notAllowed'), + }) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(500) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.body).toEqual({code: 'EACCES'}); + + done(); + }); + }); + + it('Adds torrents via URLs', (done) => { + const torrentAdded = watchTorrentList('add'); + request + .post('/api/torrents/add-urls') + .send(addTorrentByURLOptions) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + + // Continue after 15 seconds even if torrentAdded is not resolved to let the next step + // determine if the torrents have been successfully added. + Promise.race([torrentAdded, new Promise((r) => setTimeout(r, 1000 * 15))]) + .then(async () => { + // Wait a while + await new Promise((r) => setTimeout(r, 1000 * 3)); + }) + .then(() => { + done(); + }); + }); + }); + + it('GET /api/torrents to verify torrents are added via URLs', (done) => { + request + .get('/api/torrents') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end(async (err, res) => { + if (err) done(err); + + expect(res.body.torrents).not.toBeNull(); + const torrentList: TorrentList = res.body.torrents; + + const addedTorrents = Object.values(torrentList).filter((torrent) => + addTorrentByURLOptions.tags?.every((tag) => torrent.tags.includes(tag)), + ); + + expect(addedTorrents).toHaveLength(addTorrentByURLOptions.urls.length); + + await Promise.all( + addedTorrents.map(async (torrent) => { + expect(torrent.directory).toBe(addTorrentByURLOptions.destination); + + const expectedStatuses: Array = addTorrentByURLOptions.start + ? ['downloading'] + : ['stopped', 'inactive']; + expect(torrent.status).toEqual(expect.arrayContaining(expectedStatuses)); + + torrentHash = torrent.hash; + }), + ); + + done(); + }); + }); +}); + +describe('POST /api/torrents/add-files', () => { + const addTorrentByFileOptions: AddTorrentByFileOptions = { + files: torrentFiles, + destination: tempDirectory, + tags: ['testAddFiles'], + isBasePath: false, + start: false, + }; + + it('Adds torrents via files with incorrect options', (done) => { + request + .post('/api/torrents/add-files') + .send({...addTorrentByFileOptions, destination: []}) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(422) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + + done(); + }); + }); + + it('Adds torrents to disallowed path via files', (done) => { + request + .post('/api/torrents/add-files') + .send({ + ...addTorrentByFileOptions, + destination: path.join(os.tmpdir(), 'notAllowed'), + }) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(500) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + expect(res.body).toEqual({code: 'EACCES'}); + + done(); + }); + }); + + it('Adds torrents via files', (done) => { + const torrentAdded = watchTorrentList('add'); + request + .post('/api/torrents/add-files') + .send(addTorrentByFileOptions) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + + torrentAdded.then(() => { + done(); + }); + }); + }); + + it('GET /api/torrents to verify torrents are added via files', (done) => { + request + .get('/api/torrents') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end(async (err, res) => { + if (err) done(err); + + expect(res.body.torrents == null).toBe(false); + const torrentList: TorrentList = res.body.torrents; + + const addedTorrents = Object.values(torrentList).filter((torrent) => + addTorrentByFileOptions.tags?.every((tag) => torrent.tags.includes(tag)), + ); + + expect(addedTorrents).toHaveLength(addTorrentByFileOptions.files.length); + + await Promise.all( + addedTorrents.map(async (torrent) => { + expect(torrent.directory.startsWith(addTorrentByFileOptions.destination as string)).toBe(true); + }), + ); + + done(); + }); + }); +}); + +describe('POST /api/torrents/create', () => { + const createTorrentOptions: CreateTorrentOptions = { + sourcePath: tempDirectory, + tags: ['testCreate'], + trackers: testTrackers, + start: true, + isPrivate: false, + }; + + const dummyFilePath = path.join(tempDirectory, 'dummy'); + + beforeAll(() => { + fs.mkdirSync(tempDirectory, {recursive: true}); + fs.writeFileSync(dummyFilePath, 'test'); + }); + + it('Creates a multi-file torrent', (done) => { + const torrentAdded = watchTorrentList('add'); + request + .post('/api/torrents/create') + .send(createTorrentOptions) + .set('Cookie', [authToken]) + .set('Accept', 'application/x-bittorrent') + .expect(200) + .expect('Content-Type', /x-bittorrent/) + .end((err, _res) => { + if (err) done(err); + + torrentAdded.then(() => { + done(); + }); + }); + }); + + it('Creates a single-file torrent', (done) => { + const torrentAdded = watchTorrentList('add'); + request + .post('/api/torrents/create') + .send({...createTorrentOptions, sourcePath: dummyFilePath}) + .set('Cookie', [authToken]) + .set('Accept', 'application/x-bittorrent') + .expect(200) + .expect('Content-Type', /x-bittorrent/) + .end((err, _res) => { + if (err) done(err); + + torrentAdded.then(() => { + done(); + }); + }); + }); + + it('GET /api/torrents to verify torrents are added via creation', (done) => { + request + .get('/api/torrents') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end(async (err, res) => { + if (err) done(err); + + expect(res.body.torrents == null).toBe(false); + const torrentList: TorrentList = res.body.torrents; + + const addedTorrents = Object.values(torrentList).filter((torrent) => + createTorrentOptions.tags?.every((tag) => torrent.tags.includes(tag)), + ); + + expect(addedTorrents).toHaveLength(2); + + await Promise.all( + addedTorrents.map(async (torrent) => { + createdTorrentHash = torrent.hash; + expect(torrent.isPrivate).toBe(createTorrentOptions.isPrivate); + + if (process.argv.includes('--trurl')) { + // TODO: Test skipped as Transmission does not support isCompleted and isBasePath + return; + } + + expect(torrent.percentComplete).toBe(100); + }), + ); + + done(); + }); + }); +}); + +describe('PATCH /api/torrents/trackers', () => { + it('Sets single tracker', (done) => { + const setTrackersOptions: SetTorrentsTrackersOptions = { + hashes: [torrentHash], + trackers: [testTrackers[0]], + }; + + request + .patch('/api/torrents/trackers') + .send(setTrackersOptions) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + + done(); + }); + }); + + it('Sets multiple trackers', (done) => { + const setTrackersOptions: SetTorrentsTrackersOptions = { + hashes: [torrentHash], + trackers: testTrackers, + }; + + request + .patch('/api/torrents/trackers') + .send(setTrackersOptions) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, _res) => { + if (err) done(err); + + done(); + }); + }); + + it('GET /api/torrents/{hash}/trackers', (done) => { + request + .get(`/api/torrents/${torrentHash}/trackers`) + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + const trackers: Array = res.body; + expect(trackers.filter((tracker) => testTrackers.includes(tracker.url)).length).toBeGreaterThanOrEqual( + testTrackers.length, + ); + + done(); + }); + }); +}); + +describe('GET /api/torrents/{hash}/contents', () => { + it('Gets contents of torrents', (done) => { + request + .get(`/api/torrents/${torrentHash}/contents`) + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) done(err); + + const contents: Array = res.body; + + expect(Array.isArray(contents)).toBe(true); + + done(); + }); + }); +}); + +describe('POST /api/torrents/move', () => { + const destDirectory = path.join(tempDirectory, 'moved'); + + it('Moves torrent', (done) => { + const moveTorrentsOptions: MoveTorrentsOptions = { + hashes: [createdTorrentHash], + destination: destDirectory, + moveFiles: true, + isBasePath: true, + isCheckHash: true, + }; + + request + .post('/api/torrents/move') + .send(moveTorrentsOptions) + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .end(async (err, _res) => { + if (err) done(err); + + // Wait a while + await new Promise((r) => setTimeout(r, 1000 * 2)); + + expect(fs.existsSync(path.join(destDirectory, 'dummy'))).toBe(true); + + done(); + }); + }); + + it('GET /api/torrents to verify torrent is moved', (done) => { + request + .get('/api/torrents') + .send() + .set('Cookie', [authToken]) + .set('Accept', 'application/json') + .expect(200) + .expect('Content-Type', /json/) + .end(async (err, res) => { + if (err) done(err); + + expect(res.body.torrents == null).toBe(false); + const torrentList: TorrentList = res.body.torrents; + const torrent = torrentList[createdTorrentHash]; + + expect(torrent).not.toBe(null); + expect(torrent.directory).toBe(destDirectory); + expect(torrent.percentComplete).toBe(100); + + done(); + }); + }); +}); diff --git a/server/routes/api/torrents.ts b/server/routes/api/torrents.ts new file mode 100644 index 000000000..7f92b1d41 --- /dev/null +++ b/server/routes/api/torrents.ts @@ -0,0 +1,761 @@ +import childProcess from 'child_process'; +import contentDisposition from 'content-disposition'; +import createTorrent from 'create-torrent'; +import express from 'express'; +import fs from 'fs'; +import path from 'path'; +import rateLimit from 'express-rate-limit'; +import sanitize from 'sanitize-filename'; +import tar from 'tar'; + +import type { + AddTorrentByFileOptions, + AddTorrentByURLOptions, + SetTorrentsTagsOptions, +} from '@shared/schema/api/torrents'; +import type { + CheckTorrentsOptions, + CreateTorrentOptions, + DeleteTorrentsOptions, + MoveTorrentsOptions, + SetTorrentContentsPropertiesOptions, + SetTorrentsPriorityOptions, + SetTorrentsTrackersOptions, + StartTorrentsOptions, + StopTorrentsOptions, +} from '@shared/types/api/torrents'; + +import { + addTorrentByFileSchema, + addTorrentByURLSchema, + setTorrentsTagsSchema, +} from '../../../shared/schema/api/torrents'; +import {accessDeniedError, isAllowedPath, sanitizePath} from '../../util/fileUtil'; +import {getResponseFn, validationError} from '../../util/ajaxUtil'; +import {getTempPath} from '../../models/TemporaryStorage'; + +const getDestination = async ( + services: Express.Request['services'], + {destination, tags}: {destination?: string; tags?: Array}, +): Promise => { + let autoDestination = destination === '' ? undefined : destination; + + // Use preferred destination of the first tag + if (autoDestination == null) { + await services?.settingService.get('torrentDestinations').then( + ({torrentDestinations}) => { + autoDestination = torrentDestinations?.[tags?.[0] ?? '']; + }, + () => undefined, + ); + } + + // Use default destination of torrent client + if (autoDestination == null) { + const {directoryDefault} = (await services?.clientGatewayService?.getClientSettings().catch(() => undefined)) || {}; + autoDestination = directoryDefault; + } + + // Use temporary directory of Flood + if (autoDestination == null || typeof autoDestination !== 'string') { + autoDestination = getTempPath('download/'); + } + + let sanitizedPath: string | null = null; + try { + sanitizedPath = sanitizePath(autoDestination); + if (!isAllowedPath(sanitizedPath)) { + return undefined; + } + } catch (e) { + return undefined; + } + + return sanitizedPath; +}; + +const router = express.Router(); + +/** + * GET /api/torrents + * @summary Gets the list of torrents + * @tags Torrents + * @security User + * @return {TorrentListSummary} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get('/', (req, res) => { + const callback = getResponseFn(res); + + req.services?.torrentService + .fetchTorrentList() + .then((data) => { + if (data == null) { + callback(null, new Error()); + } else { + callback(data); + } + }) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * POST /api/torrents/add-urls + * @summary Adds torrents by URLs. + * @tags Torrents + * @security User + * @param {AddTorrentByURLOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.post('/add-urls', async (req, res) => { + const callback = getResponseFn(res); + + const parsedResult = addTorrentByURLSchema.safeParse(req.body); + + if (!parsedResult.success) { + validationError(res, parsedResult.error); + return; + } + + const {urls, cookies, destination, tags, isBasePath, isCompleted, start} = parsedResult.data; + + const finalDestination = await getDestination(req.services, { + destination, + tags, + }); + + if (finalDestination == null) { + callback(null, accessDeniedError()); + return; + } + + req.services?.clientGatewayService + ?.addTorrentsByURL({ + urls, + cookies: cookies != null ? cookies : {}, + destination: finalDestination, + tags: tags ?? [], + isBasePath: isBasePath ?? false, + isCompleted: isCompleted ?? false, + start: start ?? false, + }) + .then((response) => { + req.services?.torrentService.fetchTorrentList(); + return response; + }) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * POST /api/torrents/add-files + * @summary Adds torrents by files. + * @tags Torrents + * @security User + * @param {AddTorrentByFileOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.post('/add-files', async (req, res) => { + const callback = getResponseFn(res); + + const parsedResult = addTorrentByFileSchema.safeParse(req.body); + + if (!parsedResult.success) { + validationError(res, parsedResult.error); + return; + } + + const {files, destination, tags, isBasePath, isCompleted, start} = parsedResult.data; + + const finalDestination = await getDestination(req.services, { + destination, + tags, + }); + + if (finalDestination == null) { + callback(null, accessDeniedError()); + return; + } + + req.services?.clientGatewayService + ?.addTorrentsByFile({ + files, + destination: finalDestination, + tags: tags ?? [], + isBasePath: isBasePath ?? false, + isCompleted: isCompleted ?? false, + start: start ?? false, + }) + .then((response) => { + req.services?.torrentService.fetchTorrentList(); + return response; + }) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * POST /api/torrents/create + * @summary Creates a torrent + * @tags Torrents + * @security User + * @param {CreateTorrentOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/x-bittorrent + * @return {Error} 500 - failure response - application/json + */ +router.post('/create', async (req, res) => { + const {name, sourcePath, trackers, comment, infoSource, isPrivate, tags, start} = req.body; + const callback = getResponseFn(res); + + if (typeof sourcePath !== 'string') { + callback(null, accessDeniedError()); + return; + } + + const sanitizedPath = sanitizePath(sourcePath); + if (!isAllowedPath(sanitizedPath)) { + callback(null, accessDeniedError()); + return; + } + + const torrentFileName = sanitize(name || sanitizedPath.split(path.sep).pop() || `${Date.now()}`).concat('.torrent'); + const torrentPath = getTempPath(torrentFileName); + + createTorrent( + sanitizedPath, + { + name, + comment, + createdBy: 'Flood - flood.js.org', + private: isPrivate, + announceList: [trackers], + info: infoSource + ? { + source: infoSource, + } + : undefined, + }, + (err, torrent) => { + if (err) { + callback(null, err); + return; + } + + fs.promises.writeFile(torrentPath, torrent).then( + () => { + res.attachment(torrentFileName); + res.download(torrentPath); + + req.services?.clientGatewayService + ?.addTorrentsByFile({ + files: [torrent.toString('base64')], + destination: fs.lstatSync(sanitizedPath).isDirectory() ? sanitizedPath : path.dirname(sanitizedPath), + tags: tags ?? [], + isBasePath: true, + isCompleted: true, + start: start || false, + }) + .catch(() => { + // do nothing. + }); + }, + (writeErr) => { + callback(null, writeErr); + }, + ); + }, + ); +}); + +/** + * POST /api/torrents/start + * @summary Starts torrents. + * @tags Torrents + * @security User + * @param {StartTorrentsOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.post('/start', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.startTorrents(req.body) + .then((response) => { + req.services?.torrentService.fetchTorrentList(); + return response; + }) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * POST /api/torrents/stop + * @summary Stops torrents. + * @tags Torrents + * @security User + * @param {StopTorrentsOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.post('/stop', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.stopTorrents(req.body) + .then((response) => { + req.services?.torrentService.fetchTorrentList(); + return response; + }) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * POST /api/torrents/check-hash + * @summary Hash checks torrents. + * @tags Torrents + * @security User + * @param {CheckTorrentsOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.post('/check-hash', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.checkTorrents(req.body) + .then((response) => { + req.services?.torrentService.fetchTorrentList(); + return response; + }) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * POST /api/torrents/move + * @summary Moves torrents to specified destination path. + * @tags Torrents + * @security User + * @param {MoveTorrentsOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.post('/move', (req, res) => { + const callback = getResponseFn(res); + + let sanitizedPath: string | null = null; + try { + sanitizedPath = sanitizePath(req.body.destination); + if (!isAllowedPath(sanitizedPath)) { + callback(null, accessDeniedError()); + return; + } + } catch (e) { + callback(null, e); + return; + } + + req.services?.clientGatewayService + ?.moveTorrents({...req.body, destination: sanitizedPath}) + .then((response) => { + req.services?.torrentService.fetchTorrentList(); + return response; + }) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * POST /api/torrents/delete + * @summary Removes torrents from Flood. Optionally deletes data of torrents. + * @tags Torrents + * @security User + * @param {DeleteTorrentsOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.post('/delete', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.removeTorrents(req.body) + .then((response) => { + req.services?.torrentService.fetchTorrentList(); + return response; + }) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * PATCH /api/torrents/priority + * @summary Sets priority of torrents. + * @tags Torrent + * @security User + * @param {SetTorrentsPriorityOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.patch('/priority', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.setTorrentsPriority(req.body) + .then((response) => { + req.services?.torrentService.fetchTorrentList(); + return response; + }) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * PATCH /api/torrents/tags + * @summary Sets tags of torrents. + * @tags Torrents + * @security User + * @param {SetTorrentsTagsOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.patch('/tags', (req, res) => { + const parsedResult = setTorrentsTagsSchema.safeParse(req.body); + + if (!parsedResult.success) { + validationError(res, parsedResult.error); + return; + } + + req.services?.clientGatewayService?.setTorrentsTags(parsedResult.data).then( + (response) => { + req.services?.torrentService.fetchTorrentList(); + res.status(200).json(response); + }, + (err) => { + res.status(500).json(err); + }, + ); +}); + +/** + * PATCH /api/torrents/trackers + * @summary Sets trackers of torrents. + * @tags Torrents + * @security User + * @param {SetTorrentsTrackersOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.patch('/trackers', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.setTorrentsTrackers(req.body) + .then((response) => { + req.services?.torrentService.fetchTorrentList(); + return response; + }) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * + * APIs below operate on a single torrent. + * + */ + +/** + * TODO: API not yet implemented + * GET /api/torrents/{hash} + * @summary Gets information of a torrent. + * @tags Torrent + * @security User + * @param {string} hash.path - Hash of a torrent + */ + +/** + * GET /api/torrents/{hash}/contents + * @summary Gets the list of contents of a torrent and their properties. + * @tags Torrent + * @security User + * @param {string} hash.path + */ +router.get('/:hash/contents', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.getTorrentContents(req.params.hash) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * PATCH /api/torrents/{hash}/contents + * @summary Sets properties of contents of a torrent. Only priority can be set for now. + * @tags Torrent + * @security User + * @param {string} hash.path + * @param {SetTorrentContentsPropertiesOptions} request.body.required - options - application/json + * @return {object} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.patch<{hash: string}, unknown, SetTorrentContentsPropertiesOptions>('/:hash/contents', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.setTorrentContentsPriority(req.params.hash, req.body) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * GET /api/torrents/{hash}/contents/{indices}/data + * @summary Gets downloaded data of contents of a torrent. + * @tags Torrent + * @security User + * @param {string} hash.path + * @param {string} indices.path - 'all' or indices of selected contents separated by ',' + * @return {object} 200 - contents archived in .tar - application/x-tar + */ +router.get('/:hash/contents/:indices/data', (req, res) => { + const {hash, indices: stringIndices} = req.params; + const selectedTorrent = req.services?.torrentService.getTorrent(hash); + if (!selectedTorrent) return res.status(404).json({error: 'Torrent not found.'}); + + req.services?.clientGatewayService + ?.getTorrentContents(hash) + .then((contents) => { + if (!contents || contents.length < 1) { + return res.status(404).json({error: 'Torrent contents not found'}); + } + + let indices: Array; + if (!stringIndices || stringIndices === 'all') { + indices = contents.map((x) => x.index); + } else { + indices = stringIndices.split(',').map((value) => Number(value)); + } + + const filePathsToDownload = contents + .filter((content) => indices.includes(content.index)) + .map((content) => sanitizePath(path.join(selectedTorrent.directory, content.path))) + .filter((filePath) => isAllowedPath(filePath)) + .filter((filePath) => fs.existsSync(filePath)); + + if (filePathsToDownload.length < 1) { + return res.status(404).json({error: 'File not found.'}); + } + + if (filePathsToDownload.length === 1) { + const file = filePathsToDownload[0]; + + const fileName = path.basename(file); + const fileExt = path.extname(file); + + let processedType: string = fileExt; + switch (fileExt) { + // Browsers don't support MKV streaming. However, browsers do support WebM which is a + // subset of MKV. Chromium supports MKV when encoded in selected codecs. + case '.mkv': + processedType = 'video/webm'; + break; + // MIME database uses x-flac which is not recognized by browsers as streamable audio. + case '.flac': + processedType = 'audio/flac'; + break; + default: + break; + } + + res.type(processedType); + + // Allow browsers to display the content inline when only a single content is requested. + // This is useful for texts, videos and audios. Users can still download them if needed. + res.setHeader('content-disposition', contentDisposition(fileName, {type: 'inline'})); + + return res.sendFile(file); + } + + const archiveRootFolder = sanitizePath(selectedTorrent.directory); + const relativeFilePaths = filePathsToDownload.map((filePath) => + filePath.replace(`${archiveRootFolder}${path.sep}`, ''), + ); + + res.attachment(`${selectedTorrent.name}.tar`); + return tar + .c( + { + cwd: archiveRootFolder, + follow: false, + noDirRecurse: true, + portable: true, + }, + relativeFilePaths, + ) + .pipe(res); + }) + .catch((err) => { + res.status(500).json(err); + }); +}); + +/** + * GET /api/torrents/{hash}/details + * @summary Gets details of a torrent. + * @tags Torrent + * @security User + * @param {string} hash.path + */ +router.get('/:hash/details', async (req, res) => { + const callback = getResponseFn(res); + + try { + const contents = req.services?.clientGatewayService?.getTorrentContents(req.params.hash); + const peers = req.services?.clientGatewayService?.getTorrentPeers(req.params.hash); + const trackers = req.services?.clientGatewayService?.getTorrentTrackers(req.params.hash); + + await Promise.all([contents, peers, trackers]); + + callback({ + contents: await contents, + peers: await peers, + trackers: await trackers, + }); + } catch (e) { + callback(null, e); + } +}); + +/** + * GET /api/torrents/{hash}/mediainfo + * @summary Gets mediainfo output of a torrent. + * @tags Torrent + * @security User + * @param {string} hash.path + * @return {{output: string}} - 200 - success response - application/json + */ +router.get( + '/:hash/mediainfo', + // This operation is resource-intensive + // Limit each IP to 30 requests every 5 minutes + rateLimit({ + windowMs: 5 * 60 * 1000, + max: 30, + }), + async (req, res) => { + const {hash} = req.params; + const callback = getResponseFn(res); + const {torrentService} = req.services || {}; + + if (typeof hash !== 'string' || torrentService == null) { + callback(null, new Error()); + return; + } + + const {directory, name} = torrentService.getTorrent(hash); + + if (directory == null || name == null) { + callback(null, new Error()); + return; + } + + const contentPath = fs.existsSync(path.join(directory, name)) ? path.join(directory, name) : directory; + if (!isAllowedPath(contentPath)) { + callback(null, accessDeniedError()); + return; + } + + try { + const mediainfoProcess = childProcess.execFile( + 'mediainfo', + [contentPath], + {maxBuffer: 1024 * 2000, timeout: 1000 * 10}, + (error, stdout, stderr) => { + if (error) { + callback(null, error); + return; + } + + if (stderr) { + callback(null, Error(stderr)); + return; + } + + callback({output: stdout}); + }, + ); + req.on('close', () => mediainfoProcess.kill('SIGTERM')); + } catch (childProcessError) { + callback(null, Error(childProcessError)); + } + }, +); + +/** + * GET /api/torrents/{hash}/peers + * @summary Gets the list of peers of a torrent. + * @tags Torrent + * @security User + * @param {string} hash.path + */ +router.get('/:hash/peers', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.getTorrentPeers(req.params.hash) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +/** + * GET /api/torrents/{hash}/trackers + * @summary Gets the list of trackers of a torrent. + * @tags Torrent + * @security User + * @param {string} hash.path + * @return {Array} 200 - success response - application/json + * @return {Error} 500 - failure response - application/json + */ +router.get('/:hash/trackers', (req, res) => { + const callback = getResponseFn(res); + + req.services?.clientGatewayService + ?.getTorrentTrackers(req.params.hash) + .then(callback) + .catch((err) => { + callback(null, err); + }); +}); + +export default router; diff --git a/server/routes/auth.js b/server/routes/auth.js deleted file mode 100644 index 1becdb513..000000000 --- a/server/routes/auth.js +++ /dev/null @@ -1,194 +0,0 @@ -const express = require('express'); -const joi = require('joi'); -const jwt = require('jsonwebtoken'); -const passport = require('passport'); -const ajaxUtil = require('../util/ajaxUtil'); - -const requireAdmin = require('../middleware/requireAdmin'); -const config = require('../../config'); - -const router = express.Router(); -const services = require('../services'); -const Users = require('../models/Users'); - -const failedLoginResponse = 'Failed login.'; - -const setAuthToken = (res, username, isAdmin) => { - const expirationSeconds = 60 * 60 * 24 * 7; // one week - const cookieExpiration = Date.now() + expirationSeconds * 1000; - - // Create token if the password matched and no error was thrown. - const token = jwt.sign({username}, config.secret, { - expiresIn: expirationSeconds, - }); - - res.cookie('jwt', token, {expires: new Date(cookieExpiration), httpOnly: true}); - - return res.json({ - success: true, - token: `JWT ${token}`, - username, - isAdmin, - }); -}; - -const authValidation = joi.object().keys({ - username: joi.string(), - password: joi.string(), - host: joi.string(), - port: joi.string(), - socketPath: joi.string(), - isAdmin: joi.bool(), -}); - -router.use('/', (req, res, next) => { - const validation = joi.validate(req.body, authValidation); - - if (!validation.error) { - next(); - } else { - res.status(422).json({ - message: 'Validation error.', - error: validation.error, - }); - } -}); - -router.use('/users', passport.authenticate('jwt', {session: false}), requireAdmin); - -router.post('/authenticate', (req, res) => { - const credentials = { - password: req.body.password, - username: req.body.username, - }; - - Users.comparePassword(credentials, (isMatch, isAdmin, err) => { - if (isMatch != null && !err) { - return setAuthToken(res, credentials.username, isAdmin); - } - - // Incorrect username or password. - return res.status(401).json({ - message: failedLoginResponse, - }); - }); -}); - -// Allow unauthenticated registration if no users are currently registered. -router.use('/register', (req, res, next) => { - Users.initialUserGate({ - handleInitialUser: () => { - next(); - }, - handleSubsequentUser: () => { - passport.authenticate('jwt', {session: false}, (passportReq, passportRes) => { - passportRes.json({username: req.username}); - }); - }, - }); -}); - -router.post('/register', (req, res) => { - // Attempt to save the user - Users.createUser( - { - username: req.body.username, - password: req.body.password, - host: req.body.host, - port: req.body.port, - socketPath: req.body.socketPath, - isAdmin: true, - }, - (createUserResponse, createUserError) => { - if (createUserError) { - ajaxUtil.getResponseFn(res)(createUserResponse, createUserError); - return; - } - - setAuthToken(res, req.body.username, true); - }, - ); -}); - -// Allow unauthenticated verification if no users are currently registered. -router.use('/verify', (req, res, next) => { - Users.initialUserGate({ - handleInitialUser: () => { - req.initialUser = true; - next(); - }, - handleSubsequentUser: () => { - req.initialUser = false; - passport.authenticate('jwt', {session: false})(req, res, next); - }, - }); -}); - -router.get('/verify', (req, res) => { - res.json({ - initialUser: req.initialUser, - username: req.user && req.user.username, - isAdmin: req.user && req.user.isAdmin, - }); -}); - -// All subsequent routes are protected. -router.use('/', passport.authenticate('jwt', {session: false})); - -router.get('/logout', (req, res) => { - res.clearCookie('jwt').send(); -}); - -router.use('/users', (req, res, next) => { - if (req.user && req.user.isAdmin) { - next(); - return; - } - - res.status(401).send('Not authorized'); -}); - -router.get('/users', (req, res) => { - Users.listUsers(ajaxUtil.getResponseFn(res)); -}); - -router.delete('/users/:username', (req, res) => { - Users.removeUser(req.params.username, ajaxUtil.getResponseFn(res)); - services.destroyUserServices(req.user); -}); - -router.patch('/users/:username', (req, res) => { - const {username} = req.params; - const userPatch = req.body; - - if (!userPatch.socketPath) { - userPatch.socketPath = null; - } else { - userPatch.host = null; - userPatch.port = null; - } - - Users.updateUser(username, userPatch, () => { - Users.lookupUser({username}, (err, user) => { - if (err) return req.status(500).json({error: err}); - services.updateUserServices(user); - res.send(); - }); - }); -}); - -router.put('/users', (req, res) => { - Users.createUser( - { - username: req.body.username, - password: req.body.password, - host: req.body.host, - port: req.body.port, - socketPath: req.body.socketPath, - isAdmin: req.body.isAdmin, - }, - ajaxUtil.getResponseFn(res), - ); -}); - -module.exports = router; diff --git a/server/routes/client.js b/server/routes/client.js deleted file mode 100644 index 3869bd649..000000000 --- a/server/routes/client.js +++ /dev/null @@ -1,116 +0,0 @@ -const express = require('express'); -const multer = require('multer'); - -const ajaxUtil = require('../util/ajaxUtil'); -const booleanCoerce = require('../middleware/booleanCoerce'); -const client = require('../models/client'); - -const router = express.Router(); - -const upload = multer({ - dest: 'uploads/', - limits: {fileSize: 10000000}, - storage: multer.memoryStorage(), -}); - -router.get('/connection-test', (req, res) => { - req.services.clientGatewayService - .testGateway() - .then(() => { - res.status(200).json({isConnected: true}); - }) - .catch(() => { - res.status(500).json({isConnected: false}); - }); -}); - -router.post('/connection-test', (req, res) => { - req.services.clientGatewayService - .testGateway(req.body) - .then(() => { - res.status(200).json({isConnected: true}); - }) - .catch(() => { - res.status(500).json({isConnected: false}); - }); -}); - -router.post('/add', (req, res) => { - client.addUrls(req.user, req.services, req.body, ajaxUtil.getResponseFn(res)); -}); - -router.post('/add-files', upload.array('torrents'), booleanCoerce('isBasePath'), (req, res) => { - client.addFiles(req.user, req.services, req, ajaxUtil.getResponseFn(res)); -}); - -router.get('/settings', (req, res) => { - client.getSettings(req.user, req.services, req.query, ajaxUtil.getResponseFn(res)); -}); - -router.patch('/settings', (req, res) => { - client.setSettings(req.user, req.services, req.body, ajaxUtil.getResponseFn(res)); -}); - -router.put('/settings/speed-limits', (req, res) => { - client.setSpeedLimits(req.user, req.services, req.body, ajaxUtil.getResponseFn(res)); -}); - -router.post('/start', (req, res) => { - client.startTorrent(req.user, req.services, req.body.hashes, ajaxUtil.getResponseFn(res)); -}); - -router.post('/stop', (req, res) => { - client.stopTorrent(req.user, req.services, req.body.hashes, ajaxUtil.getResponseFn(res)); -}); - -router.post('/torrent-details', (req, res) => { - client.getTorrentDetails(req.user, req.services, req.body.hash, ajaxUtil.getResponseFn(res)); -}); - -router.patch('/torrents/:hash/priority', (req, res) => { - client.setPriority(req.user, req.services, req.params.hash, req.body, ajaxUtil.getResponseFn(res)); -}); - -router.patch('/torrents/:hash/file-priority', (req, res) => { - client.setFilePriority(req.user, req.services, req.params.hash, req.body, ajaxUtil.getResponseFn(res)); -}); - -router.post('/torrents/check-hash', (req, res) => { - client.checkHash(req.user, req.services, req.body.hash, ajaxUtil.getResponseFn(res)); -}); - -router.post('/torrents/move', (req, res) => { - client.moveTorrents(req.user, req.services, req.body, ajaxUtil.getResponseFn(res)); -}); - -router.post('/torrents/delete', (req, res) => { - const {deleteData, hash: hashes} = req.body; - const callback = ajaxUtil.getResponseFn(res); - - req.services.clientGatewayService - .removeTorrents({hashes, deleteData}) - .then(callback) - .catch(err => { - callback(null, err); - }); -}); - -router.patch('/torrents/taxonomy', (req, res) => { - client.setTaxonomy(req.user, req.services, req.body, ajaxUtil.getResponseFn(res)); -}); - -router.get('/methods.json', (req, res) => { - const {type} = req.query; - const {args} = req.query; - let method = 'system.listMethods'; - - if (type === 'help') { - method = 'system.methodHelp'; - } else if (type === 'signature') { - method = 'system.methodSignature'; - } - - client.listMethods(req.user, req.services, method, args, ajaxUtil.getResponseFn(res)); -}); - -module.exports = router; diff --git a/server/services/BaseService.js b/server/services/BaseService.js deleted file mode 100644 index 5cd4907eb..000000000 --- a/server/services/BaseService.js +++ /dev/null @@ -1,22 +0,0 @@ -const EventEmitter = require('events'); - -class BaseService extends EventEmitter { - constructor(user, services, ...eventEmitterConfig) { - super(...eventEmitterConfig); - if (!user || !user._id) throw new Error('Missing user ID'); - this.user = user; - this.services = services; - } - - destroy() { - delete this.services; - delete this.user; - } - - updateUser(user, services) { - this.user = user; - this.services = services; - } -} - -module.exports = BaseService; diff --git a/server/services/BaseService.ts b/server/services/BaseService.ts new file mode 100644 index 000000000..fe7787a98 --- /dev/null +++ b/server/services/BaseService.ts @@ -0,0 +1,37 @@ +import {EventEmitter} from 'events'; +import type TypedEmitter from 'typed-emitter'; + +import type {UserInDatabase} from '@shared/schema/Auth'; + +import type {UserServices} from '.'; + +class BaseService extends (EventEmitter as { + new (): TypedEmitter; +}) { + user: UserInDatabase; + services?: UserServices; + + constructor(user: UserInDatabase) { + super(); + this.user = user; + } + + destroy() { + delete this.services; + } + + onServicesUpdated = () => { + // do nothing. + }; + + updateUser(user: UserInDatabase) { + this.user = user; + } + + updateServices(service?: UserServices) { + this.services = service; + this.onServicesUpdated(); + } +} + +export default BaseService; diff --git a/server/services/Transmission/clientGatewayService.ts b/server/services/Transmission/clientGatewayService.ts new file mode 100644 index 000000000..6f8223a07 --- /dev/null +++ b/server/services/Transmission/clientGatewayService.ts @@ -0,0 +1,488 @@ +import geoip from 'geoip-country'; + +import type { + AddTorrentByFileOptions, + AddTorrentByURLOptions, + SetTorrentsTagsOptions, +} from '@shared/schema/api/torrents'; +import type { + CheckTorrentsOptions, + DeleteTorrentsOptions, + MoveTorrentsOptions, + SetTorrentContentsPropertiesOptions, + SetTorrentsPriorityOptions, + SetTorrentsTrackersOptions, + StartTorrentsOptions, + StopTorrentsOptions, +} from '@shared/types/api/torrents'; +import type {ClientSettings} from '@shared/types/ClientSettings'; +import type {TorrentContent} from '@shared/types/TorrentContent'; +import type {TorrentList, TorrentListSummary, TorrentProperties} from '@shared/types/Torrent'; +import type {TorrentPeer} from '@shared/types/TorrentPeer'; +import type {TorrentTracker} from '@shared/types/TorrentTracker'; +import type {TransferSummary} from '@shared/types/TransferData'; +import type {TransmissionConnectionSettings} from '@shared/schema/ClientConnectionSettings'; +import type {SetClientSettingsOptions} from '@shared/types/api/client'; + +import ClientGatewayService from '../interfaces/clientGatewayService'; +import ClientRequestManager from './clientRequestManager'; +import {getDomainsFromURLs} from '../../util/torrentPropertiesUtil'; +import {TorrentContentPriority} from '../../../shared/types/TorrentContent'; +import {TorrentPriority} from '../../../shared/types/Torrent'; +import torrentPropertiesUtil from './util/torrentPropertiesUtil'; +import {TorrentTrackerType} from '../../../shared/types/TorrentTracker'; +import {TransmissionPriority, TransmissionTorrentsSetArguments} from './types/TransmissionTorrentsMethods'; + +class TransmissionClientGatewayService extends ClientGatewayService { + clientRequestManager = new ClientRequestManager(this.user.client as TransmissionConnectionSettings); + + async addTorrentsByFile({ + files, + destination, + tags, + isCompleted, + start, + }: Required): Promise { + const addedTorrents: [string, ...string[]] = await Promise.all( + files.map(async (file) => { + const {hashString} = + (await this.clientRequestManager + .addTorrent({ + metainfo: file, + 'download-dir': destination, + paused: !start, + }) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .catch(() => undefined)) || {}; + return hashString; + }), + ) + .then((results) => results.filter((hash) => hash != null) as string[]) + .then((hashes) => { + if (hashes.length < 1) { + throw new Error(); + } + return hashes as [string, ...string[]]; + }); + + if (tags.length > 0) { + await this.setTorrentsTags({hashes: addedTorrents, tags}); + } + + if (isCompleted) { + // Transmission doesn't support skipping verification + this.checkTorrents({hashes: addedTorrents}).catch(() => undefined); + } + } + + async addTorrentsByURL({ + urls, + cookies, + destination, + tags, + isCompleted, + start, + }: Required): Promise { + const addedTorrents: [string, ...string[]] = await Promise.all( + urls.map(async (url) => { + const domain = url.split('/')[2]; + const {hashString} = + (await this.clientRequestManager + .addTorrent({ + filename: url, + cookies: cookies[domain] != null ? `${cookies[domain].join('; ')};` : undefined, + 'download-dir': destination, + paused: !start, + }) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .catch(() => undefined)) || {}; + return hashString; + }), + ) + .then((results) => results.filter((hash) => hash != null) as string[]) + .then((hashes) => { + if (hashes.length < 1) { + throw new Error(); + } + return hashes as [string, ...string[]]; + }); + + if (tags.length > 0) { + await this.setTorrentsTags({hashes: addedTorrents, tags}); + } + + if (isCompleted) { + // Transmission doesn't support skipping verification + this.checkTorrents({hashes: addedTorrents}).catch(() => undefined); + } + } + + async checkTorrents({hashes}: CheckTorrentsOptions): Promise { + return this.clientRequestManager + .verifyTorrents(hashes) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async getTorrentContents(hash: TorrentProperties['hash']): Promise> { + return this.clientRequestManager + .getTorrents(hash, ['files', 'fileStats']) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((torrents) => { + const [torrent] = torrents; + if (torrent == null) { + return Promise.reject(); + } + + const {files, fileStats} = torrent; + if (files.length !== fileStats.length) { + return Promise.reject(); + } + + const torrentContents: Array = files.map((file, index) => { + const stat = fileStats[index]; + + let priority = TorrentContentPriority.NORMAL; + if (!stat.wanted) { + priority = TorrentContentPriority.DO_NOT_DOWNLOAD; + } else if (stat.priority === TransmissionPriority.TR_PRI_HIGH) { + priority = TorrentContentPriority.HIGH; + } + + return { + index, + path: file.name, + filename: file.name.split('/').pop() as string, + percentComplete: (file.bytesCompleted / file.length) * 100, + priority, + sizeBytes: file.length, + }; + }); + + return torrentContents; + }); + } + + async getTorrentPeers(hash: TorrentProperties['hash']): Promise> { + return this.clientRequestManager + .getTorrents(hash, ['peers']) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((torrents) => { + const [torrent] = torrents; + if (torrent == null) { + return Promise.reject(); + } + + const torrentPeers: Array = torrent.peers + .filter((peer) => peer.isDownloadingFrom || peer.isUploadingTo) + .map((peer) => ({ + address: peer.address, + country: geoip.lookup(peer.address)?.country || '', + clientVersion: peer.clientName, + completedPercent: peer.progress * 100, + downloadRate: peer.rateToClient, + uploadRate: peer.rateToPeer, + isEncrypted: peer.isEncrypted, + isIncoming: peer.isIncoming, + })); + + return torrentPeers; + }); + } + + async getTorrentTrackers(hash: TorrentProperties['hash']): Promise> { + return this.clientRequestManager + .getTorrents(hash, ['trackerStats']) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((torrents) => { + const [torrent] = torrents; + if (torrent == null) { + return Promise.reject(); + } + + const torrentTrackers: Array = torrent.trackerStats.map((tracker) => ({ + url: tracker.announce, + type: tracker.announce.startsWith('udp') ? TorrentTrackerType.UDP : TorrentTrackerType.HTTP, + })); + + return torrentTrackers; + }); + } + + async moveTorrents({hashes, destination, moveFiles}: MoveTorrentsOptions): Promise { + return this.clientRequestManager + .setTorrentsLocation(hashes, destination, moveFiles) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async removeTorrents({hashes, deleteData}: DeleteTorrentsOptions): Promise { + return this.clientRequestManager + .removeTorrents(hashes, deleteData) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async setTorrentsPriority({hashes, priority}: SetTorrentsPriorityOptions): Promise { + let transmissionPriority = TransmissionPriority.TR_PRI_NORMAL; + + switch (priority) { + case TorrentPriority.DO_NOT_DOWNLOAD: + return undefined; + case TorrentPriority.LOW: + transmissionPriority = TransmissionPriority.TR_PRI_LOW; + break; + case TorrentPriority.HIGH: + transmissionPriority = TransmissionPriority.TR_PRI_HIGH; + break; + default: + break; + } + + return this.clientRequestManager + .setTorrentsProperties({ + ids: hashes, + bandwidthPriority: transmissionPriority, + }) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async setTorrentsTags({hashes, tags}: SetTorrentsTagsOptions): Promise { + return this.clientRequestManager + .setTorrentsProperties({ids: hashes, labels: tags}) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async setTorrentsTrackers({hashes, trackers}: SetTorrentsTrackersOptions): Promise { + // Remove existing trackers + await this.clientRequestManager + .getTorrents(hashes, ['trackers']) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((torrents) => + Promise.all( + torrents.map(async (torrent, index) => { + await this.clientRequestManager + .setTorrentsProperties({ + ids: hashes[index], + trackerRemove: torrent.trackers.map((tracker) => tracker.id), + }) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .catch(() => undefined); + }), + ), + ); + + return this.clientRequestManager + .setTorrentsProperties({ids: hashes, trackerAdd: trackers}) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async setTorrentContentsPriority( + hash: string, + {indices, priority}: SetTorrentContentsPropertiesOptions, + ): Promise { + let wantedArgument: keyof TransmissionTorrentsSetArguments = 'files-wanted'; + let priorityArgument: keyof TransmissionTorrentsSetArguments = 'priority-normal'; + + switch (priority) { + case TorrentContentPriority.DO_NOT_DOWNLOAD: + wantedArgument = 'files-unwanted'; + break; + case TorrentContentPriority.HIGH: + priorityArgument = 'priority-high'; + break; + default: + break; + } + + return this.clientRequestManager + .setTorrentsProperties({ + ids: hash, + [wantedArgument]: indices, + [priorityArgument]: indices, + }) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async startTorrents({hashes}: StartTorrentsOptions): Promise { + return this.clientRequestManager + .startTorrents(hashes) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async stopTorrents({hashes}: StopTorrentsOptions): Promise { + return this.clientRequestManager + .stopTorrents(hashes) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async fetchTorrentList(): Promise { + return this.clientRequestManager + .getTorrents(null, [ + 'hashString', + 'downloadDir', + 'name', + 'haveValid', + 'addedDate', + 'dateCreated', + 'rateDownload', + 'rateUpload', + 'downloadedEver', + 'uploadedEver', + 'eta', + 'isPrivate', + 'error', + 'errorString', + 'peersGettingFromUs', + 'peersSendingToUs', + 'status', + 'totalSize', + 'trackers', + 'labels', + ]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(async (torrents) => { + this.emit('PROCESS_TORRENT_LIST_START'); + + const torrentList: TorrentList = Object.assign( + {}, + ...(await Promise.all( + torrents.map(async (torrent) => { + const percentComplete = (torrent.haveValid / torrent.totalSize) * 100; + const ratio = torrent.downloadedEver === 0 ? -1 : torrent.uploadedEver / torrent.downloadedEver; + const trackerURIs = getDomainsFromURLs(torrent.trackers.map((tracker) => tracker.announce)); + const status = torrentPropertiesUtil.getTorrentStatus(torrent); + + const torrentProperties: TorrentProperties = { + hash: torrent.hashString, + name: torrent.name, + bytesDone: torrent.haveValid, + dateAdded: torrent.addedDate, + dateCreated: torrent.dateCreated, + directory: torrent.downloadDir, + downRate: torrent.rateDownload, + downTotal: torrent.downloadedEver, + upRate: torrent.rateUpload, + upTotal: torrent.uploadedEver, + eta: torrent.eta, + isPrivate: torrent.isPrivate, + message: torrent.errorString, + peersConnected: torrent.peersGettingFromUs, + peersTotal: torrent.peersGettingFromUs, + percentComplete, + priority: TorrentPriority.NORMAL, + ratio, + seedsConnected: torrent.peersSendingToUs, + seedsTotal: torrent.peersSendingToUs, + sizeBytes: torrent.totalSize, + status, + tags: torrent.labels || [], + trackerURIs, + }; + + this.emit('PROCESS_TORRENT', torrentProperties); + + return { + [torrentProperties.hash]: torrentProperties, + }; + }), + )), + ); + + const torrentListSummary = { + id: Date.now(), + torrents: torrentList, + }; + + this.emit('PROCESS_TORRENT_LIST_END', torrentListSummary); + return torrentListSummary; + }); + } + + async fetchTransferSummary(): Promise { + return this.clientRequestManager + .getSessionStats() + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((stats) => { + return { + downRate: stats.downloadSpeed, + downTotal: stats['current-stats'].downloadedBytes, + upRate: stats.uploadSpeed, + upTotal: stats['current-stats'].uploadedBytes, + }; + }); + } + + async getClientSettings(): Promise { + return this.clientRequestManager + .getSessionProperties([ + 'dht-enabled', + 'peer-port', + 'download-dir', + 'peer-port-random-on-start', + 'pex-enabled', + 'speed-limit-down', + 'speed-limit-down-enabled', + 'speed-limit-up', + 'speed-limit-up-enabled', + 'peer-limit-global', + 'peer-limit-per-torrent', + 'seed-queue-enabled', + 'seed-queue-size', + ]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((properties) => { + const clientSettings: ClientSettings = { + dht: properties['dht-enabled'], + dhtPort: properties['peer-port'], + directoryDefault: properties['download-dir'], + networkHttpMaxOpen: 0, + networkLocalAddress: [], + networkMaxOpenFiles: 0, + networkPortOpen: true, + networkPortRandom: properties['peer-port-random-on-start'], + networkPortRange: `${properties['peer-port']}`, + piecesHashOnCompletion: false, + piecesMemoryMax: 0, + protocolPex: properties['pex-enabled'], + throttleGlobalDownSpeed: properties['speed-limit-down-enabled'] ? properties['speed-limit-down'] * 1024 : 0, + throttleGlobalUpSpeed: properties['speed-limit-up-enabled'] ? properties['speed-limit-up'] * 1024 : 0, + throttleMaxPeersNormal: 0, + throttleMaxPeersSeed: 0, + throttleMaxDownloads: 0, + throttleMaxDownloadsGlobal: 0, + throttleMaxUploads: 0, + throttleMaxUploadsGlobal: properties['seed-queue-enabled'] ? properties['seed-queue-size'] : 0, + throttleMinPeersNormal: 0, + throttleMinPeersSeed: 0, + trackersNumWant: 0, + }; + + return clientSettings; + }); + } + + async setClientSettings(settings: SetClientSettingsOptions): Promise { + return this.clientRequestManager + .setSessionProperties({ + 'dht-enabled': settings.dht, + 'download-dir': settings.directoryDefault, + 'peer-port': settings.networkPortRange ? Number(settings.networkPortRange?.split('-')[0]) : undefined, + 'peer-port-random-on-start': settings.networkPortRandom, + 'pex-enabled': settings.protocolPex, + 'speed-limit-down-enabled': settings.throttleGlobalDownSpeed !== 0, + 'speed-limit-down': + settings.throttleGlobalDownSpeed != null ? settings.throttleGlobalDownSpeed / 1024 : undefined, + 'speed-limit-up-enabled': settings.throttleGlobalUpSpeed !== 0, + 'speed-limit-up': settings.throttleGlobalUpSpeed != null ? settings.throttleGlobalUpSpeed / 1024 : undefined, + 'seed-queue-enabled': settings.throttleMaxUploadsGlobal !== 0, + 'seed-queue-size': settings.throttleMaxUploadsGlobal, + }) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async testGateway(): Promise { + return this.clientRequestManager + .updateSessionID() + .then(() => this.processClientRequestSuccess(undefined), this.processClientRequestError); + } +} + +export default TransmissionClientGatewayService; diff --git a/server/services/Transmission/clientRequestManager.ts b/server/services/Transmission/clientRequestManager.ts new file mode 100644 index 000000000..8c8d686a6 --- /dev/null +++ b/server/services/Transmission/clientRequestManager.ts @@ -0,0 +1,297 @@ +import axios, {AxiosError} from 'axios'; + +import type {TransmissionConnectionSettings} from '@shared/schema/ClientConnectionSettings'; + +import type { + TransmissionSessionGetArguments, + TransmissionSessionProperties, + TransmissionSessionSetArguments, + TransmissionSessionStats, +} from './types/TransmissionSessionMethods'; +import { + TransmissionTorrentIDs, + TransmissionTorrentProperties, + TransmissionTorrentAddArguments, + TransmissionTorrentsGetArguments, + TransmissionTorrentsRemoveArguments, + TransmissionTorrentsSetArguments, + TransmissionTorrentsSetLocationArguments, +} from './types/TransmissionTorrentsMethods'; + +class ClientRequestManager { + private rpcURL: string; + private authHeader: string; + private sessionID?: Promise; + + async fetchSessionID(url = this.rpcURL, authHeader = this.authHeader): Promise { + return axios + .get(url, { + headers: { + Authorization: authHeader, + }, + }) + .then( + () => { + return undefined; + }, + (err: AxiosError) => { + if (err.response?.status === 409) { + return err.response?.headers['x-transmission-session-id']; + } + throw err; + }, + ); + } + + async updateSessionID(url = this.rpcURL, authHeader = this.authHeader): Promise { + let authFailed = false; + + this.sessionID = new Promise((resolve) => { + this.fetchSessionID(url, authHeader).then( + (sessionID) => { + resolve(sessionID); + }, + () => { + authFailed = true; + resolve(undefined); + }, + ); + }); + + await this.sessionID; + + return authFailed ? Promise.reject() : Promise.resolve(); + } + + async getRequestHeaders(): Promise> { + const sessionID = await this.sessionID; + + return { + Authorization: this.authHeader, + ...(sessionID == null ? {} : {'X-Transmission-Session-Id': sessionID}), + }; + } + + async getSessionStats(): Promise { + return axios + .post( + this.rpcURL, + {method: 'session-stats'}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + return res.data.arguments; + }); + } + + async getSessionProperties( + fields: T, + ): Promise> { + const sessionGetArguments: TransmissionSessionGetArguments = {fields}; + + return axios + .post( + this.rpcURL, + {method: 'session-get', arguments: sessionGetArguments}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + return res.data.arguments; + }); + } + + async setSessionProperties(properties: TransmissionSessionSetArguments): Promise { + return axios + .post( + this.rpcURL, + {method: 'session-set', arguments: properties}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + }); + } + + async getTorrents( + ids: TransmissionTorrentIDs | null, + fields: T, + ): Promise>> { + const torrentsGetArguments: TransmissionTorrentsGetArguments = { + ids: ids || undefined, + fields, + format: 'objects', + }; + + return axios + .post( + this.rpcURL, + {method: 'torrent-get', arguments: torrentsGetArguments}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success' || res.data.arguments.torrents == null) { + throw new Error(); + } + return res.data.arguments.torrents; + }); + } + + async addTorrent( + args: TransmissionTorrentAddArguments, + ): Promise> { + return axios + .post( + this.rpcURL, + {method: 'torrent-add', arguments: args}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + return res.data.arguments['torrent-added']; + }); + } + + async setTorrentsProperties(args: TransmissionTorrentsSetArguments): Promise { + return axios + .post( + this.rpcURL, + {method: 'torrent-set', arguments: args}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + }); + } + + async startTorrents(ids: TransmissionTorrentIDs): Promise { + return axios + .post( + this.rpcURL, + {method: 'torrent-start-now', arguments: {ids}}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + }); + } + + async stopTorrents(ids: TransmissionTorrentIDs): Promise { + return axios + .post( + this.rpcURL, + {method: 'torrent-stop', arguments: {ids}}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + }); + } + + async verifyTorrents(ids: TransmissionTorrentIDs): Promise { + return axios + .post( + this.rpcURL, + {method: 'torrent-verify', arguments: {ids}}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + }); + } + + async removeTorrents(ids: TransmissionTorrentIDs, deleteData?: boolean): Promise { + const removeTorrentsArguments: TransmissionTorrentsRemoveArguments = { + ids, + 'delete-local-data': deleteData, + }; + + return axios + .post( + this.rpcURL, + {method: 'torrent-remove', arguments: removeTorrentsArguments}, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + }); + } + + async setTorrentsLocation(ids: TransmissionTorrentIDs, location: string, move?: boolean): Promise { + const torrentsSetLocationArguments: TransmissionTorrentsSetLocationArguments = { + ids, + location, + move, + }; + + return axios + .post( + this.rpcURL, + { + method: 'torrent-set-location', + arguments: torrentsSetLocationArguments, + }, + { + headers: await this.getRequestHeaders(), + }, + ) + .then((res) => { + if (res.data.result !== 'success') { + throw new Error(); + } + }); + } + + constructor(connectionSettings: TransmissionConnectionSettings) { + const {url, username, password} = connectionSettings; + + this.rpcURL = url; + this.authHeader = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; + + this.updateSessionID().catch(() => undefined); + setInterval(() => { + this.updateSessionID().catch(() => undefined); + }, 1000 * 60 * 60 * 8); + } +} + +export default ClientRequestManager; diff --git a/server/services/Transmission/types/TransmissionSessionMethods.ts b/server/services/Transmission/types/TransmissionSessionMethods.ts new file mode 100644 index 000000000..90fcd2952 --- /dev/null +++ b/server/services/Transmission/types/TransmissionSessionMethods.ts @@ -0,0 +1,152 @@ +import type {TransmissionTorrentIDs} from './TransmissionTorrentsMethods'; + +interface TransmissionSessionUnits { + 'speed-units': Array; + 'speed-bytes': Array; + 'size-units': Array; + 'size-bytes': Array; + 'memory-units': Array; + 'memory-bytes': Array; +} + +export interface TransmissionSessionProperties { + // max global download speed (KBps) + 'alt-speed-down': number; + // true means use the alt speeds + 'alt-speed-enabled': boolean; + // when to turn on alt speeds (units: minutes after midnight) + 'alt-speed-time-begin': number; + // true means the scheduled on/off times are used + 'alt-speed-time-enabled': boolean; + // when to turn off alt speeds (units: same) + 'alt-speed-time-end': number; + // what day(s) to turn on alt speeds (look at tr_sched_day) + 'alt-speed-time-day': number; + // max global upload speed (KBps) + 'alt-speed-up': number; + // location of the blocklist to use for "blocklist-update" + 'blocklist-url': string; + // true means enabled + 'blocklist-enabled': boolean; + // number of rules in the blocklist + 'blocklist-size': number; + // maximum size of the disk cache (MB) + 'cache-size-mb': number; + // location of transmission's configuration directory + 'config-dir': string; + // default path to download torrents + 'download-dir': string; + // max number of torrents to download at once (see download-queue-enabled) + 'download-queue-size': number; + // if true, limit how many torrents can be downloaded at once + 'download-queue-enabled': boolean; + // true means allow dht in public torrents + 'dht-enabled': boolean; + // "required", "preferred", "tolerated" + encryption: string; + // torrents we're seeding will be stopped if they're idle for this long + 'idle-seeding-limit': number; + // true if the seeding inactivity limit is honored by default + 'idle-seeding-limit-enabled': boolean; + // path for incomplete torrents, when enabled + 'incomplete-dir': string; + // true means keep torrents in incomplete-dir until done + 'incomplete-dir-enabled': boolean; + // true means allow Local Peer Discovery in public torrents + 'lpd-enabled': boolean; + // maximum global number of peers + 'peer-limit-global': number; + // maximum global number of peers + 'peer-limit-per-torrent': number; + // true means allow pex in public torrents + 'pex-enabled': boolean; + // port number + 'peer-port': number; + // true means pick a random peer port on launch + 'peer-port-random-on-start': boolean; + // true means enabled + 'port-forwarding-enabled': boolean; + // whether or not to consider idle torrents as stalled + 'queue-stalled-enabled': boolean; + // torrents that are idle for N minuets aren't counted toward seed-queue-size or download-queue-size + 'queue-stalled-minutes': number; + // true means append ".part" to incomplete files + 'rename-partial-files': boolean; + // the current RPC API version + 'rpc-version': number; + // the minimum RPC API version supported + 'rpc-version-minimum': number; + // filename of the script to run + 'script-torrent-done-filename': string; + // whether or not to call the "done" script + 'script-torrent-done-enabled': boolean; + // the default seed ratio for torrents to use + seedRatioLimit: number; + // true if seedRatioLimit is honored by default + seedRatioLimited: boolean; + // max number of torrents to uploaded at once (see seed-queue-enabled) + 'seed-queue-size': number; + // if true, limit how many torrents can be uploaded at once + 'seed-queue-enabled': boolean; + // max global download speed (KBps) + 'speed-limit-down': number; + // true means enabled + 'speed-limit-down-enabled': boolean; + // max global upload speed (KBps) + 'speed-limit-up': number; + // true means enabled + 'speed-limit-up-enabled': boolean; + // true means added torrents will be started right away + 'start-added-torrents': boolean; + // true means the .torrent file of added torrents will be deleted + 'trash-original-torrent-files': boolean; + // {TransmissionSessionUnits} + units: TransmissionSessionUnits; + // true means allow utp + 'utp-enabled': boolean; + // long version string "$version ($revision)" + version: string; +} + +// Method name: "session-get" +export interface TransmissionSessionGetArguments { + // fields to be fetched. + fields: Array; +} +// Method name: "session-set" +export type TransmissionSessionSetArguments = Partial< + Omit< + TransmissionSessionProperties, + 'blocklist-size' | 'config-dir' | 'rpc-version' | 'rpc-version-minimum' | 'version' | 'session-id' + > +>; + +interface TransmissionSessionHistory { + uploadedBytes: number; + downloadedBytes: number; + filesAdded: number; + sessionCount: number; + secondsActive: number; +} + +// Method name: "session-stats" +export interface TransmissionSessionStats { + torrentCount: number; + activeTorrentCount: number; + pausedTorrentCount: number; + downloadSpeed: number; + uploadSpeed: number; + 'cumulative-stats': TransmissionSessionHistory; + 'current-stats': TransmissionSessionHistory; +} + +// Method name: "queue-move-top" | "queue-move-up" | "queue-move-down" | "queue-move-bottom" +export interface TransmissionQueueMoveArguments { + ids: TransmissionTorrentIDs; +} + +// Method name: "free-space" +// This method tests how much free space is available in a client-specified folder. +export interface TransmissionFreeSpaceArguments { + path: string; +} diff --git a/server/services/Transmission/types/TransmissionTorrentsMethods.ts b/server/services/Transmission/types/TransmissionTorrentsMethods.ts new file mode 100644 index 000000000..e852385b8 --- /dev/null +++ b/server/services/Transmission/types/TransmissionTorrentsMethods.ts @@ -0,0 +1,360 @@ +export enum TransmissionPriority { + TR_PRI_LOW = -1, + TR_PRI_NORMAL = 0, + TR_PRI_HIGH = 1, +} + +interface TransmissionTorrentContent { + bytesCompleted: number; + length: number; + name: string; +} + +// a file's non-constant properties. +interface TransmissionTorrentContentStats { + bytesCompleted: number; + wanted: boolean; + priority: TransmissionPriority; +} + +export enum TransmissionTorrentError { + // everything's fine + TR_STAT_OK = 0, + // when we anounced to the tracker, we got a warning in the response + TR_STAT_TRACKER_WARNING = 1, + // when we anounced to the tracker, we got an error in the response + TR_STAT_TRACKER_ERROR = 2, + // local trouble, such as disk full or permissions error + TR_STAT_LOCAL_ERROR = 3, +} + +export enum TransmissionTorrentStatus { + // Torrent is stopped + TR_STATUS_STOPPED = 0, + // Queued to check files + TR_STATUS_CHECK_WAIT = 1, + // Checking files + TR_STATUS_CHECK = 2, + // Queued to download + TR_STATUS_DOWNLOAD_WAIT = 3, + // Downloading + TR_STATUS_DOWNLOAD = 4, + // Queued to seed + TR_STATUS_SEED_WAIT = 5, + // Seeding + TR_STATUS_SEED = 6, +} + +interface TransmissionTorrentPeer { + address: string; + clientName: string; + clientIsChoked: boolean; + clientIsInterested: boolean; + flagStr: string; + isDownloadingFrom: boolean; + isEncrypted: boolean; + isIncoming: boolean; + isUploadingTo: boolean; + isUTP: boolean; + peerIsChoked: boolean; + peerIsInterested: boolean; + port: number; + progress: number; + // B/s + rateToClient: number; + // B/s + rateToPeer: number; +} + +interface TransmissionTorrentPeersFrom { + fromCache: number; + fromDht: number; + fromIncoming: number; + fromLpd: number; + fromLtep: number; + fromPex: number; + fromTracker: number; +} + +interface TransmissionTorrentTracker { + announce: string; + id: number; + scrape: string; + tier: number; +} + +interface TransmissionTorrentTrackerStats { + announce: string; + announceState: number; + downloadCount: number; + hasAnnounced: boolean; + hasScraped: boolean; + host: string; + id: number; + isBackup: boolean; + lastAnnouncePeerCount: number; + lastAnnounceResult: string; + lastAnnounceStartTime: number; + lastAnnounceSucceeded: boolean; + lastAnnounceTime: number; + lastAnnounceTimedOut: boolean; + lastScrapeResult: string; + lastScrapeStartTime: number; + lastScrapeSucceeded: boolean; + lastScrapeTime: number; + lastScrapeTimedOut: boolean; + leecherCount: number; + nextAnnounceTime: number; + nextScrapeTime: number; + scrape: string; + scrapeState: number; + seederCount: number; + tier: number; +} + +export interface TransmissionTorrentProperties { + // The last time we uploaded or downloaded piece data on this torrent. + activityDate: number; + // When the torrent was first added. + addedDate: number; + bandwidthPriority: TransmissionPriority; + comment: string; + // Byte count of all the corrupt data you've ever downloaded for this torrent. + corruptEver: number; + creator: string; + dateCreated: number; + // Byte count of all the piece data we want and don't have yet, but that a connected peer does have. [0...leftUntilDone] + desiredAvailable: number; + // When the torrent finished downloading. + doneDate: number; + downloadDir: string; + // Byte count of all the non-corrupt data you've ever downloaded for this torrent. + downloadedEver: number; + downloadLimit: number; + downloadLimited: boolean; + // The last time during this session that a rarely-changing field changed. + editDate: number; + error: TransmissionTorrentError; + // A warning or error message regarding the torrent. + errorString: string; + // If downloading, estimated number of seconds left until the torrent is done. + // If seeding, estimated number of seconds left until seed ratio is reached. + eta: number; + // If seeding, number of seconds left until the idle time limit is reached. + etaIdle: number; + 'file-count': number; + files: Array; + fileStats: Array; + hashString: string; + // Byte count of all the partial piece data we have for this torrent. As pieces become complete, + // this value may decrease as portions of it are moved to `corrupt' or `haveValid'. + haveUnchecked: number; + // Byte count of all the checksum-verified data we have for this torrent. + haveValid: number; + honorsSessionLimits: boolean; + // IDs are good as simple lookup keys, but are not persistent between sessions. + id: number; + isFinished: boolean; + isPrivate: boolean; + isStalled: boolean; + labels: Array; + // Byte count of how much data is left to be downloaded until we've got all the pieces that we want. [0...tr_info.sizeWhenDone] + leftUntilDone: number; + magnetLink: string; + // time when one or more of the torrent's trackers will allow you to manually ask for more peers, or 0 if you can't. + manualAnnounceTime: number; + maxConnectedPeers: number; + // How much of the metadata the torrent has. For torrents added from a .torrent this will always be 1. + // For magnet links, this number will from from 0 to 1 as the metadata is downloaded. Range is [0..1] + metadataPercentComplete: number; + // The torrent's name. + name: string; + 'peer-limit': number; + peers: Array; + // Number of peers that we're connected to + peersConnected: number; + // How many peers we found out about from the tracker, or from pex, or from incoming connections, or from our resume file. + peersFrom: TransmissionTorrentPeersFrom; + // Number of peers that we're sending data to + peersGettingFromUs: number; + // Number of peers that are sending data to us. + peersSendingToUs: number; + // How much has been downloaded of the files the user wants. This differs from percentComplete + // if the user wants only some of the torrent's files. Range is [0..1] + percentDone: number; + // A bitfield holding pieceCount flags which are set to 'true' if we have the piece matching + // that position. This is a base64-encoded string. + pieces: string; + pieceCount: number; + pieceSize: number; + // an array of tr_info.filecount numbers. each is the tr_priority_t mode for the corresponding file. + priorities: Array; + 'primary-mime-type': string; + // This torrent's queue position. All torrents have a queue position, even if it's not queued. + queuePosition: number; + // B/s + rateDownload: number; + // B/s + rateUpload: number; + // When tr_stat.activity is TR_STATUS_CHECK or TR_STATUS_CHECK_WAIT, this is the percentage of + // how much of the files has been verified. When it gets to 1, the verify process is done. Range is [0..1] + recheckProgress: number; + // Cumulative seconds the torrent's ever spent downloading + secondsDownloading: number; + // Cumulative seconds the torrent's ever spent seeding + secondsSeeding: number; + seedIdleLimit: number; + seedIdleMode: number; + seedRatioLimit: number; + seedRatioMode: number; + // Byte count of all the piece data we'll have downloaded when we're done, whether or not we have + // it yet. This may be less than tr_info.totalSize if only some of the torrent's files are wanted. [0...tr_info.totalSize] + sizeWhenDone: number; + // When the torrent was last started. + startDate: number; + status: TransmissionTorrentStatus; + trackers: Array; + trackerStats: Array; + // total size of the torrent, in bytes + totalSize: number; + torrentFile: string; + // Byte count of all data you've ever uploaded for this torrent. + uploadedEver: number; + uploadLimit: number; + uploadLimited: boolean; + uploadRatio: number; + // an array of tr_info.fileCount 'booleans' true if the corresponding file is to be downloaded. + wanted: Array; + webseeds: Array; + // Number of webseeds that are sending data to us. + webseedsSendingToUs: number; +} + +// number representing torrent id or string representing torrent hash or an array thereof. +export type TransmissionTorrentIDs = Array | string | number; + +// Method name: "torrent-get" +export interface TransmissionTorrentsGetArguments { + // torrent list. All torrents are used if the "ids" argument is omitted. + ids?: TransmissionTorrentIDs; + // fields to be fetched. + fields: Array; + // how to format the "torrents" response field. + format: 'objects' | 'table'; +} + +// Method name: "torrent-set" +export interface TransmissionTorrentsSetArguments { + // torrent list. All torrents are used if the "ids" argument is omitted. + ids?: TransmissionTorrentIDs; + // this torrent's bandwidth tr_priority_t + bandwidthPriority?: TransmissionPriority; + // maximum download speed (KBps) + downloadLimit?: number; + // true if "downloadLimit" is honored + downloadLimited?: boolean; + // indices of file(s) to download + 'files-wanted'?: Array; + // indices of file(s) to not download + 'files-unwanted'?: Array; + // true if session upload limits are honored + honorsSessionLimits?: boolean; + // array of string labels + labels?: Array; + // new location of the torrent's content + location?: string; + // maximum number of peers + 'peer-limit'?: number; + // indices of high-priority file(s) + 'priority-high'?: Array; + // indices of low-priority file(s) + 'priority-low'?: Array; + // indices of normal-priority file(s) + 'priority-normal'?: Array; + // position of this torrent in its queue [0...n) + queuePosition?: number; + // torrent-level number of minutes of seeding inactivity + seedIdleLimit?: number; + // which seeding inactivity to use. See tr_idlelimit + seedIdleMode?: number; + // torrent-level seeding ratio + seedRatioLimit?: number; + // which ratio to use. See tr_ratiolimit + seedRatioMode?: number; + // strings of announce URLs to add + trackerAdd?: Array; + // ids of trackers to remove + trackerRemove?: Array; + // pairs of , [0, url, 1, url, ...] + trackerReplace?: Array; + // maximum upload speed (KBps) + uploadLimit?: number; + // true if "uploadLimit" is honored + uploadLimited?: boolean; +} + +// Method name: "torrent-add" +interface TransmissionTorrentAddCommonArguments { + // path to download the torrent to + 'download-dir'?: string; + // if true, don't start the torrent + paused?: boolean; + // maximum number of peers + 'peer-limit'?: number; + // torrent's bandwidth tr_priority_t + bandwidthPriority?: TransmissionPriority; + // indices of file(s) to download + 'files-wanted'?: Array; + // indices of file(s) to not download + 'files-unwanted'?: Array; + // indices of high-priority file(s) + 'priority-high'?: Array; + // indices of low-priority file(s) + 'priority-low'?: Array; + // indices of normal-priority file(s) + 'priority-normal'?: Array; +} + +interface TransmissionTorrentAddByURLArguments extends TransmissionTorrentAddCommonArguments { + // filename or URL of the .torrent file + filename: string; + // pointer to a string of one or more cookies. + // The format of the "cookies" should be NAME=CONTENTS, where NAME is the cookie name and CONTENTS + // is what the cookie should contain. Set multiple cookies like this: "name1=content1; name2=content2;". + cookies?: string; +} + +interface TransmissionTorrentAddByFileArguments extends TransmissionTorrentAddCommonArguments { + // base64-encoded .torrent content + metainfo: string; +} + +export type TransmissionTorrentAddArguments = + | TransmissionTorrentAddByURLArguments + | TransmissionTorrentAddByFileArguments; + +// Method name: "torrent-remove" +export interface TransmissionTorrentsRemoveArguments { + ids: TransmissionTorrentIDs; + // delete local data. (default: false) + 'delete-local-data'?: boolean; +} + +// Method name: "torrent-set-location" +export interface TransmissionTorrentsSetLocationArguments { + ids: TransmissionTorrentIDs; + // the new torrent location + location: string; + // if true, move from previous location. otherwise, search "location" for files. (default: false) + move?: boolean; +} + +// Method name: "torrent-rename-path" +export interface TransmissionTorrentRenamePathArguments { + // must only be 1 torrent + ids: TransmissionTorrentIDs; + // the path to the file or folder that will be renamed + path: string; + // the file or folder's new name + name: string; +} diff --git a/server/services/Transmission/util/torrentPropertiesUtil.ts b/server/services/Transmission/util/torrentPropertiesUtil.ts new file mode 100644 index 000000000..05814161d --- /dev/null +++ b/server/services/Transmission/util/torrentPropertiesUtil.ts @@ -0,0 +1,56 @@ +import {TransmissionTorrentError, TransmissionTorrentStatus} from '../types/TransmissionTorrentsMethods'; + +import type {TorrentProperties} from '../../../../shared/types/Torrent'; +import type {TransmissionTorrentProperties} from '../types/TransmissionTorrentsMethods'; + +const getTorrentStatus = ( + properties: Pick< + TransmissionTorrentProperties, + 'error' | 'status' | 'rateDownload' | 'rateUpload' | 'haveValid' | 'totalSize' + >, +): TorrentProperties['status'] => { + const {error, status, rateDownload, rateUpload, haveValid, totalSize} = properties; + const statuses: TorrentProperties['status'] = []; + + switch (status) { + case TransmissionTorrentStatus.TR_STATUS_CHECK: + case TransmissionTorrentStatus.TR_STATUS_CHECK_WAIT: + statuses.push('checking'); + break; + case TransmissionTorrentStatus.TR_STATUS_DOWNLOAD: + case TransmissionTorrentStatus.TR_STATUS_DOWNLOAD_WAIT: + statuses.push('downloading'); + if (rateDownload > 0) { + statuses.push('active'); + } else { + statuses.push('inactive'); + } + break; + case TransmissionTorrentStatus.TR_STATUS_SEED: + case TransmissionTorrentStatus.TR_STATUS_SEED_WAIT: + statuses.push('seeding'); + if (rateUpload > 0) { + statuses.push('active'); + } else { + statuses.push('inactive'); + } + break; + case TransmissionTorrentStatus.TR_STATUS_STOPPED: + statuses.push('stopped', 'inactive'); + break; + default: + break; + } + + if (error !== TransmissionTorrentError.TR_STAT_OK) { + statuses.push('error'); + } + + if (haveValid === totalSize) { + statuses.push('complete'); + } + + return statuses; +}; + +export default {getTorrentStatus}; diff --git a/server/services/clientGatewayService.js b/server/services/clientGatewayService.js deleted file mode 100644 index ac4ed3607..000000000 --- a/server/services/clientGatewayService.js +++ /dev/null @@ -1,261 +0,0 @@ -const path = require('path'); -const rimraf = require('rimraf'); - -const BaseService = require('./BaseService'); -const clientGatewayServiceEvents = require('../constants/clientGatewayServiceEvents'); -const fileListPropMap = require('../constants/fileListPropMap'); -const methodCallUtil = require('../util/methodCallUtil'); -const scgiUtil = require('../util/scgiUtil'); - -const fileListMethodCallConfig = methodCallUtil.getMethodCallConfigFromPropMap(fileListPropMap, ['pathComponents']); - -class ClientGatewayService extends BaseService { - constructor(...serviceConfig) { - super(...serviceConfig); - - this.hasError = null; - this.torrentListReducers = []; - this.processClientRequestError = this.processClientRequestError.bind(this); - this.processClientRequestSuccess = this.processClientRequestSuccess.bind(this); - } - - /** - * Adds a reducer to be applied when processing the torrent list. - * - * @param {Object} reducer - The reducer object - * @param {string} reducer.key - The key of the reducer, to be applied to the - * torrent list object. - * @param {function} reducer.reduce - The actual reducer. This will recevie - * the entire processed torrent list response and it should return it own - * processed value, to be assigned to the provided key. - */ - addTorrentListReducer(reducer = {}) { - if (typeof reducer.key !== 'string') { - throw new Error('reducer.key must be a string.'); - } - - if (typeof reducer.reduce !== 'function') { - throw new Error('reducer.reduce must be a function.'); - } - - this.torrentListReducers.push(reducer); - } - - removeTorrents(options = {hashes: [], deleteData: false}) { - const methodCalls = options.hashes.reduce((accumulator, hash, index) => { - let eraseFileMethodCallIndex = index; - - // If we're deleting files, we grab each torrents' file list before we - // remove them. - if (options.deleteData) { - // We offset the indices of these method calls so that we know exactly - // where to retrieve the responses in the future. - const directoryBaseMethodCallIndex = index + options.hashes.length; - // We also need to ensure that the erase method call occurs after - // our request for information. - eraseFileMethodCallIndex = index + options.hashes.length * 2; - - accumulator[index] = { - methodName: 'f.multicall', - params: [hash, ''].concat(fileListMethodCallConfig.methodCalls), - }; - - accumulator[directoryBaseMethodCallIndex] = { - methodName: 'd.directory_base', - params: [hash], - }; - } - - accumulator[eraseFileMethodCallIndex] = { - methodName: 'd.erase', - params: [hash], - }; - - return accumulator; - }, []); - - return this.services.clientRequestManager - .methodCall('system.multicall', [methodCalls]) - .then(response => { - if (options.deleteData) { - const torrentCount = options.hashes.length; - const filesToDelete = options.hashes.reduce((accumulator, hash, hashIndex) => { - const fileList = response[hashIndex][0]; - const directoryBase = response[hashIndex + torrentCount][0]; - - const torrentFilesToDelete = fileList.reduce((fileListAccumulator, file) => { - // We only look at the first path component returned because - // if it's a directory within the torrent, then we'll remove - // the entire directory. - const filePath = path.join(directoryBase, file[0][0]); - - // filePath might be a directory, so it may have already been - // added. If not, we add it. - if (!fileListAccumulator.includes(filePath)) { - fileListAccumulator.push(filePath); - } - - return fileListAccumulator; - }, []); - - return accumulator.concat(torrentFilesToDelete); - }, []); - - filesToDelete.forEach(file => { - rimraf(file, {disableGlob: true}, error => { - if (error) { - console.error(`Error deleting file: ${file}\n${error}`); - } - }); - }); - } - - this.emit(clientGatewayServiceEvents.TORRENTS_REMOVED); - - return response; - }) - .catch(this.processClientError); - } - - /** - * Sends a multicall request to rTorrent with the requested method calls. - * - * @param {Object} options - An object of options... - * @param {Array} options.methodCalls - An array of strings representing - * method calls, which the client uses to retrieve details. - * @param {Array} options.propLabels - An array of strings that are used as - * keys for the transformed torrent details. - * @param {Array} options.valueTransformations - An array of functions that - * will be called with the values as returned by the client. These return - * values will be assigned to the key from the propLabels array. - * @return {Promise} - Resolves with the processed client response or rejects - * with the processed client error. - */ - fetchTorrentList(options) { - return this.services.clientRequestManager - .methodCall('d.multicall2', ['', 'main'].concat(options.methodCalls)) - .then(this.processClientRequestSuccess) - .then(torrents => this.processTorrentListResponse(torrents, options)) - .catch(this.processClientRequestError); - } - - fetchTransferSummary(options) { - const methodCalls = options.methodCalls.map(methodName => ({methodName, params: []})); - - return this.services.clientRequestManager - .methodCall('system.multicall', [methodCalls]) - .then(this.processClientRequestSuccess) - .then(transferRate => this.processTransferRateResponse(transferRate, options)) - .catch(this.processClientRequestError); - } - - processClientRequestSuccess(response) { - if (this.hasError == null || this.hasError === true) { - this.hasError = false; - this.emit(clientGatewayServiceEvents.CLIENT_CONNECTION_STATE_CHANGE); - } - - return response; - } - - processClientRequestError(error) { - if (!this.hasError) { - this.hasError = true; - this.emit(clientGatewayServiceEvents.CLIENT_CONNECTION_STATE_CHANGE); - } - throw error; - } - - /** - * After rTorrent responds with the requested torrent details, we construct - * an object with hashes as keys and processed details as values. - * - * @param {Array} response - The array of all torrents and their details. - * @param {Object} options - An object of options that instruct us how to - * process the client's response. - * @param {Array} options.propLabels - An array of strings that map to the - * method call. These are the keys of the torrent details. - * @param {Array} options.valueTransformations - An array of functions that - * transform the detail from the client's response. - * @return {Object} - An object that represents all torrents with hashes as - * keys, each value being an object of detail labels and values. - */ - processTorrentListResponse(torrentList, options) { - this.emit(clientGatewayServiceEvents.PROCESS_TORRENT_LIST_START); - - // We map the array of details to objects with sensibly named keys. We want - // to return an object with torrent hashes as keys and an object of torrent - // details as values. - const processedTorrentList = torrentList.reduce( - (listAccumulator, torrentDetailValues) => { - // Transform the array of torrent detail values to an object with - // sensibly named keys. - const processedTorrentDetailValues = torrentDetailValues.reduce((valueAccumulator, value, valueIndex) => { - const key = options.propLabels[valueIndex]; - const transformValue = options.valueTransformations[valueIndex]; - - valueAccumulator[key] = transformValue(value); - return valueAccumulator; - }, {}); - - // Assign values from external reducers to the torrent list object. - this.torrentListReducers.forEach(reducer => { - const {key, reduce} = reducer; - - processedTorrentDetailValues[key] = reduce(processedTorrentDetailValues); - }); - - listAccumulator.torrents[processedTorrentDetailValues.hash] = processedTorrentDetailValues; - - this.emit(clientGatewayServiceEvents.PROCESS_TORRENT, processedTorrentDetailValues); - - return listAccumulator; - }, - {torrents: {}}, - ); - - // Provide the number of torrents. - processedTorrentList.length = torrentList.length; - // Provide a unique ID for this specific torrent list. - processedTorrentList.id = Date.now(); - - this.emit(clientGatewayServiceEvents.PROCESS_TORRENT_LIST_END, processedTorrentList); - - return processedTorrentList; - } - - processTransferRateResponse(transferRate = [], options) { - this.emit(clientGatewayServiceEvents.PROCESS_TRANSFER_RATE_START); - - return transferRate.reduce((accumulator, value, index) => { - const key = options.propLabels[index]; - const transformValue = options.valueTransformations[index]; - - accumulator[key] = transformValue(value); - - return accumulator; - }, {}); - } - - testGateway(clientSettings) { - if (!clientSettings) { - return this.services.clientRequestManager - .methodCall('system.methodExist', ['system.multicall']) - .then(this.processClientRequestSuccess) - .catch(this.processClientRequestError); - } - - return scgiUtil.methodCall( - { - socket: clientSettings.socket, - socketPath: clientSettings.socketPath, - port: clientSettings.port, - host: clientSettings.host, - }, - 'system.methodExist', - ['system.multicall'], - ); - } -} - -module.exports = ClientGatewayService; diff --git a/server/services/clientRequestManager.js b/server/services/clientRequestManager.js deleted file mode 100644 index 4b1d0550d..000000000 --- a/server/services/clientRequestManager.js +++ /dev/null @@ -1,83 +0,0 @@ -const BaseService = require('./BaseService'); -const scgiUtil = require('../util/scgiUtil'); - -class ClientRequestManager extends BaseService { - constructor(...serviceConfig) { - super(...serviceConfig); - - this.isRequestPending = false; - this.lastResponseTimestamp = 0; - this.pendingRequests = []; - - this.sendDefferedMethodCall = this.sendDefferedMethodCall.bind(this); - this.sendMethodCall = this.sendMethodCall.bind(this); - this.methodCall = this.methodCall.bind(this); - } - - handleRequestEnd() { - this.isRequestPending = false; - - // We avoid initiating any deffered requests until at least 250ms have - // since the previous response. - const currentTimestamp = Date.now(); - const timeSinceLastResponse = currentTimestamp - this.lastResponseTimestamp; - - if (timeSinceLastResponse <= 250) { - const delay = 250 - timeSinceLastResponse; - setTimeout(this.sendDefferedMethodCall, delay); - this.lastResponseTimestamp = currentTimestamp + delay; - } else { - this.sendDefferedMethodCall(); - this.lastResponseTimestamp = currentTimestamp; - } - } - - sendDefferedMethodCall() { - if (this.pendingRequests.length > 0) { - this.isRequestPending = true; - - const nextRequest = this.pendingRequests.shift(); - - this.sendMethodCall(nextRequest.methodName, nextRequest.parameters) - .then(nextRequest.resolve) - .catch(nextRequest.reject); - } - } - - sendMethodCall(methodName, parameters) { - const connectionMethod = { - host: this.user.host, - port: this.user.port, - socketPath: this.user.socketPath, - }; - - return scgiUtil - .methodCall(connectionMethod, methodName, parameters) - .then(response => { - this.handleRequestEnd(); - return response; - }) - .catch(error => { - this.handleRequestEnd(); - throw error; - }); - } - - methodCall(methodName, parameters) { - // We only allow one request at a time. - if (this.isRequestPending) { - return new Promise((resolve, reject) => { - this.pendingRequests.push({ - methodName, - parameters, - resolve, - reject, - }); - }); - } - this.isRequestPending = true; - return this.sendMethodCall(methodName, parameters); - } -} - -module.exports = ClientRequestManager; diff --git a/server/services/diskUsageService.js b/server/services/diskUsageService.js deleted file mode 100644 index affa509ac..000000000 --- a/server/services/diskUsageService.js +++ /dev/null @@ -1,139 +0,0 @@ -/** - * This service is not per rtorrent session, which is why it does not inherit - * `BaseService` nor have any use of the per user API ie. `getSerivce()` - */ -const EventEmitter = require('events'); -const util = require('util'); -const execFile = util.promisify(require('child_process').execFile); -const config = require('../../config'); -const diskUsageServiceEvents = require('../constants/diskUsageServiceEvents'); - -const PLATFORMS_SUPPORTED = ['darwin', 'linux', 'freebsd', 'win32']; -const MAX_BUFFER_SIZE = 65536; - -const filterMountPoint = - config.diskUsageService && config.diskUsageService.watchMountPoints - ? // if user has configured watchPartitions filter each line output for given - // array - mountpoint => config.diskUsageService.watchMountPoints.includes(mountpoint) - : () => true; // include all mounted file systems by default - -const linuxDfParsing = () => - execFile('df | tail -n+2', { - shell: true, - maxBuffer: MAX_BUFFER_SIZE, - }).then(({stdout}) => - stdout - .trim() - .split('\n') - .map(disk => disk.split(/\s+/)) - .filter(disk => filterMountPoint(disk[5])) - .map(([_fs, size, used, avail, _pcent, target]) => { - return { - size: Number.parseInt(size, 10) * 1024, - used: Number.parseInt(used, 10) * 1024, - avail: Number.parseInt(avail, 10) * 1024, - target, - }; - }), - ); - -const diskUsage = { - linux: linuxDfParsing, - freebsd: linuxDfParsing, - darwin: () => - execFile('df -kl | tail -n+2', { - shell: true, - maxBuffer: MAX_BUFFER_SIZE, - }).then(({stdout}) => - stdout - .trim() - .split('\n') - .map(disk => disk.split(/\s+/)) - .filter(disk => filterMountPoint(disk[8])) - .map(([_fs, size, used, avail, _pcent, _iused, _ifree, _piused, target]) => { - return { - size: Number.parseInt(size, 10) * 1024, - used: Number.parseInt(used, 10) * 1024, - avail: Number.parseInt(avail, 10) * 1024, - target, - }; - }), - ), - win32: () => - execFile('wmic logicaldisk', { - shell: true, - maxBuffer: MAX_BUFFER_SIZE, - }).then(({stdout}) => - stdout - .trim() - .split('\n') - .slice(1) - .map(disk => disk.split(/\s+/)) - .filter(disk => filterMountPoint(disk[1])) - .map(disk => ({ - size: disk[14], - used: disk[14] - disk[10], - avail: disk[10], - target: disk[1], - })), - ), -}; - -const INTERVAL_UPDATE = 10000; - -class DiskUsageService extends EventEmitter { - constructor() { - super(); - this.disks = []; - this.tLastChange = 0; - this.interval = 0; - - if (!PLATFORMS_SUPPORTED.includes(process.platform)) { - console.log(`warning: DiskUsageService is only supported in ${PLATFORMS_SUPPORTED.join()}`); - return; - } - - // start polling disk usage when the first listener is added - this.on('newListener', event => { - if ( - this.listenerCount(diskUsageServiceEvents.DISK_USAGE_CHANGE) === 0 && - event === diskUsageServiceEvents.DISK_USAGE_CHANGE - ) { - this.updateInterval = setInterval(this.updateDisks.bind(this), INTERVAL_UPDATE); - } - }); - - // stop polling disk usage when the last listener is removed - this.on('removeListener', event => { - if ( - this.listenerCount(diskUsageServiceEvents.DISK_USAGE_CHANGE) === 0 && - event === diskUsageServiceEvents.DISK_USAGE_CHANGE - ) { - clearInterval(this.updateInterval); - } - }); - } - - updateDisks() { - if (!PLATFORMS_SUPPORTED.includes(process.platform)) { - return Promise.resolve([]); - } - return diskUsage[process.platform]().then(disks => { - if (disks.length !== this.disks.length || disks.some((d, i) => d.used !== this.disks[i].used)) { - this.tLastChange = Date.now(); - this.disks = disks; - this.emit(diskUsageServiceEvents.DISK_USAGE_CHANGE, this.getDiskUsage()); - } - }); - } - - getDiskUsage() { - return { - id: this.tLastChange, - disks: this.disks, - }; - } -} - -module.exports = new DiskUsageService(); diff --git a/server/services/feedService.js b/server/services/feedService.js deleted file mode 100644 index 17854b293..000000000 --- a/server/services/feedService.js +++ /dev/null @@ -1,365 +0,0 @@ -const path = require('path'); -const Datastore = require('nedb'); - -const BaseService = require('./BaseService'); -const client = require('../models/client'); -const config = require('../../config'); -const Feed = require('../models/Feed'); -const regEx = require('../../shared/util/regEx'); - -// TODO: Allow users to specify which key contains the URLs. -const getTorrentUrlsFromItem = feedItem => { - // If we've got an Array of enclosures, we'll iterate over the values and - // look for the url key. - if (feedItem.enclosures && Array.isArray(feedItem.enclosures)) { - return feedItem.enclosures.reduce((urls, enclosure) => { - if (enclosure.url) { - urls.push(enclosure.url); - } - - return urls; - }, []); - } - - // If we've got a Object of enclosures, use url key - if (feedItem.enclosure && feedItem.enclosure.url) { - return [feedItem.enclosure.url]; - } - - // If there are no enclosures, then use the link tag instead - if (feedItem.link) { - // remove CDATA tags around links - const cdata = regEx.cdata.exec(feedItem.link); - - if (cdata && cdata[1]) { - return [cdata[1]]; - } - - return [feedItem.link]; - } - - return []; -}; - -const getItemsMatchingRules = (feedItems, rules, feed) => { - return feedItems.reduce((matchedItems, feedItem) => { - rules.forEach(rule => { - const isMatched = new RegExp(rule.match, 'gi').test(feedItem[rule.field]); - const isExcluded = rule.exclude !== '' && new RegExp(rule.exclude, 'gi').test(feedItem[rule.field]); - - if (isMatched && !isExcluded) { - const torrentUrls = getTorrentUrlsFromItem(feedItem); - const isAlreadyDownloaded = matchedItems.some(matchedItem => - torrentUrls.every(url => matchedItem.urls.includes(url)), - ); - - if (!isAlreadyDownloaded) { - matchedItems.push({ - urls: torrentUrls, - tags: rule.tags, - feedID: rule.feedID, - feedLabel: feed.label, - matchTitle: feedItem.title, - ruleID: rule._id, - ruleLabel: rule.label, - destination: rule.destination, - startOnLoad: rule.startOnLoad, - }); - } - } - }); - - return matchedItems; - }, []); -}; - -const getUrlsFromItems = feedItems => { - return feedItems.reduce((urls, feedItem) => urls.concat(feedItem.urls), []); -}; - -class FeedService extends BaseService { - constructor(...serviceConfig) { - super(...serviceConfig); - - this.isDBReady = false; - this.db = this.loadDatabase(); - - this.init(); - } - - addFeed(feed, callback) { - this.addItem('feed', feed, newFeed => { - this.startNewFeed(newFeed); - callback(newFeed); - }); - } - - modifyFeed(id, feedToModify, callback) { - const modifiedFeed = this.feeds.find(feed => feed.options._id === id); - modifiedFeed.stopReader(); - modifiedFeed.modify(feedToModify); - this.modifyItem(id, feedToModify, err => { - callback(err); - }); - } - - addItem(type, item, callback) { - if (!this.isDBReady) return; - - this.db.insert(Object.assign(item, {type}), (err, newDoc) => { - if (err) { - callback(null, err); - return; - } - - callback(newDoc); - }); - } - - modifyItem(id, newItem, callback) { - if (!this.isDBReady) { - return; - } - - this.db.update({_id: id}, {$set: newItem}, {}, err => { - if (err) { - callback(null, err); - return; - } - - callback(null); - }); - } - - addRule(rule, callback) { - this.addItem('rule', rule, (newRule, error) => { - if (error) { - callback(null, error); - return; - } - - callback(newRule); - - if (this.rules[newRule.feedID] == null) { - this.rules[newRule.feedID] = []; - } - - this.rules[newRule.feedID].push(newRule); - - const associatedFeed = this.feeds.find(feed => feed.options._id === newRule.feedID); - - if (associatedFeed) { - this.handleNewItems({ - feed: associatedFeed.options, - items: associatedFeed.getItems(), - }); - } - }); - } - - getAll(query, callback) { - query = query || {}; - - this.db.find({}, (err, docs) => { - if (err) { - callback(null, err); - return; - } - - callback( - docs.reduce((memo, item) => { - const type = `${item.type}s`; - - if (memo[type] == null) { - memo[type] = []; - } - - memo[type].push(item); - - return memo; - }, {}), - ); - }); - } - - getFeeds(query, callback) { - this.queryItem('feed', query, callback); - } - - getItems(query, callback) { - const selectedFeed = this.feeds.find(feed => feed.options._id === query.id); - - if (selectedFeed) { - const items = selectedFeed.getItems(); - - if (query.search) { - callback(items.filter(item => item.title.toLowerCase().indexOf(query.search.toLowerCase()) !== -1)); - } else { - callback(items); - } - } else { - callback(null); - } - } - - getPreviouslyMatchedUrls() { - return new Promise((resolve, reject) => { - this.db.find({type: 'matchedTorrents'}, (err, docs) => { - if (err) { - reject(err); - } - - resolve(docs.reduce((matchedUrls, doc) => matchedUrls.concat(doc.urls), [])); - }); - }); - } - - getRules(query, callback) { - this.queryItem('rule', query, callback); - } - - handleNewItems({items: feedItems, feed}) { - this.getPreviouslyMatchedUrls() - .then(previouslyMatchedUrls => { - const applicableRules = this.rules[feed._id]; - if (!applicableRules) return; - - const itemsMatchingRules = getItemsMatchingRules(feedItems, applicableRules, feed); - const itemsToDownload = itemsMatchingRules.filter(item => - item.urls.some(url => !previouslyMatchedUrls.includes(url)), - ); - - const lastAddUrlCallback = () => { - const urlsToAdd = getUrlsFromItems(itemsToDownload); - - this.db.update({type: 'matchedTorrents'}, {$push: {urls: {$each: urlsToAdd}}}, {upsert: true}); - - this.services.notificationService.addNotification( - itemsToDownload.map(item => ({ - id: 'notification.feed.downloaded.torrent', - data: { - feedLabel: item.feedLabel, - ruleLabel: item.ruleLabel, - title: item.matchTitle, - }, - })), - ); - this.services.torrentService.fetchTorrentList(); - }; - - itemsToDownload.forEach((item, index) => { - client.addUrls( - this.user, - this.services, - { - urls: item.urls, - destination: item.destination, - start: item.startOnLoad, - tags: item.tags, - }, - () => { - if (index === itemsToDownload.length - 1) { - lastAddUrlCallback(); - } - - this.db.update({_id: item.ruleID}, {$inc: {count: 1}}, {upsert: true}); - }, - ); - }); - }) - .catch(console.error); - } - - init() { - this.feeds = []; - this.rules = {}; - this.db.find({}, (err, docs) => { - if (err) { - return; - } - - // Create two arrays, one for feeds and one for rules. - const feedsSummary = docs.reduce( - (accumulator, doc) => { - if (doc.type === 'feed' || doc.type === 'rule') { - accumulator[`${doc.type}s`].push(doc); - } - - return accumulator; - }, - {feeds: [], rules: []}, - ); - - // Add all download rules to the local state. - feedsSummary.rules.forEach(rule => { - if (this.rules[rule.feedID] == null) { - this.rules[rule.feedID] = []; - } - - this.rules[rule.feedID].push(rule); - }); - - // Initiate all feeds. - feedsSummary.feeds.forEach(feed => { - this.startNewFeed(feed); - }); - }); - } - - loadDatabase() { - if (this.isDBReady) return; - - const db = new Datastore({ - autoload: true, - filename: path.join(config.dbPath, this.user._id, 'settings', 'feeds.db'), - }); - this.isDBReady = true; - return db; - } - - queryItem(type, query, callback) { - query = query || {}; - - this.db.find(Object.assign(query, {type}), (err, docs) => { - if (err) { - callback(null, err); - return; - } - - callback(docs); - }); - } - - removeItem(id, callback) { - let indexToRemove = -1; - const itemToRemove = this.feeds.find((feed, index) => { - if (feed.options._id === id) { - indexToRemove = index; - return true; - } - - return false; - }); - - if (itemToRemove != null) { - itemToRemove.stopReader(); - this.feeds.splice(indexToRemove, 1); - } - - this.db.remove({_id: id}, {}, (err, docs) => { - if (err) { - callback(null, err); - return; - } - - callback(docs); - }); - } - - startNewFeed(feedConfig) { - feedConfig.onNewItems = this.handleNewItems.bind(this); - this.feeds.push(new Feed(feedConfig)); - } -} - -module.exports = FeedService; diff --git a/server/services/feedService.ts b/server/services/feedService.ts new file mode 100644 index 000000000..42564f005 --- /dev/null +++ b/server/services/feedService.ts @@ -0,0 +1,424 @@ +import path from 'path'; +import Datastore from 'nedb'; + +import type {FeedItem} from 'feedsub'; + +import BaseService from './BaseService'; +import config from '../../config'; +import FeedReader from '../models/FeedReader'; +import {getFeedItemsMatchingRules, getTorrentUrlsFromFeedItem} from '../util/feedUtil'; + +import type {AddFeedOptions, AddRuleOptions, ModifyFeedOptions} from '../../shared/types/api/feed-monitor'; +import type {Feed, Item, MatchedTorrents, Rule} from '../../shared/types/Feed'; +import type {FeedReaderOptions} from '../models/FeedReader'; + +class FeedService extends BaseService { + db = this.loadDatabase(); + feedReaders: Array = []; + rules: { + [feedID: string]: Array; + } = {}; + + constructor(...args: ConstructorParameters) { + super(...args); + + this.onServicesUpdated = () => { + this.init(); + }; + } + + /** + * Subscribes to a feed + * + * @param {AddFeedOptions} options - An object of options... + * @return {Promise} - Resolves with Feed or rejects with error. + */ + async addFeed({url, label, interval}: AddFeedOptions): Promise { + if (typeof url !== 'string' || typeof label !== 'string' || typeof interval !== 'number') { + return Promise.reject(); + } + + if (this.db == null) { + return Promise.reject(); + } + + const newFeed = await new Promise((resolve, reject) => { + this.db.insert({type: 'feed', url, label, interval}, (err, newDoc) => { + if (err) { + reject(err); + return; + } + + resolve(newDoc as Feed); + }); + }); + + this.startNewFeed(newFeed); + + return newFeed; + } + + /** + * Modifies the options of a feed subscription + * + * @param {string} id - Unique ID of the feed + * @param {ModifyFeedOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + async modifyFeed(id: string, {url, label, interval}: ModifyFeedOptions): Promise { + if (url != null && typeof url !== 'string') { + return Promise.reject(); + } + + if (label != null && typeof label !== 'string') { + return Promise.reject(); + } + + if (interval != null && typeof interval !== 'number') { + return Promise.reject(); + } + + const modifiedFeedReader = this.feedReaders.find((feedReader) => feedReader.getOptions().feedID === id); + + if (modifiedFeedReader == null || this.db == null) { + return Promise.reject(); + } + + // JSON.parse(JSON.stringify()) to remove undefined properties + + modifiedFeedReader.stopReader(); + modifiedFeedReader.modify(JSON.parse(JSON.stringify({feedLabel: label, url, interval}))); + + return new Promise((resolve, reject) => { + this.db.update({_id: id}, {$set: JSON.parse(JSON.stringify({label, url, interval}))}, {}, (err) => { + if (err) { + reject(err); + return; + } + + resolve(); + }); + }); + } + + async addRule(options: AddRuleOptions): Promise { + const newRule = await new Promise((resolve, reject) => { + this.db.insert({type: 'rule', ...options}, (err, newDoc) => { + if (err) { + reject(err); + return; + } + + resolve(newDoc as Rule); + }); + }); + + if (this.rules[newRule.feedIDs[0]] == null) { + this.rules[newRule.feedIDs[0]] = []; + } + + this.rules[newRule.feedIDs[0]].push(newRule); + + const associatedFeedReader = this.feedReaders.find( + (feedReader) => feedReader.getOptions().feedID === newRule.feedIDs[0], + ); + + if (associatedFeedReader) { + this.handleNewItems(associatedFeedReader.getOptions(), associatedFeedReader.getItems()); + } + + return newRule; + } + + async getAll(): Promise<{feeds: Array; rules: Array}> { + if (this.db == null) { + return Promise.reject(); + } + + return new Promise<{feeds: Array; rules: Array}>((resolve, reject) => { + this.db.find({}, (err: Error | null, docs: Array) => { + if (err) { + reject(err); + return; + } + + resolve( + docs.reduce( + (memo: {feeds: Array; rules: Array}, item) => { + if (item.type === 'feed') { + memo.feeds.push(item); + } + + if (item.type === 'rule') { + memo.rules.push(item); + } + + return memo; + }, + {feeds: [], rules: []}, + ), + ); + }); + }); + } + + async getFeeds(id?: string): Promise> { + return new Promise>((resolve, reject) => { + this.db.find(id ? {_id: id} : {type: 'feed'}, (err: Error | null, feeds: Array) => { + if (err) { + reject(err); + return; + } + + resolve(feeds); + }); + }); + } + + async getItems(id: string, search: string): Promise> { + const selectedFeedReader = this.feedReaders.find((feedReader) => feedReader.getOptions().feedID === id); + + if (selectedFeedReader == null) { + return Promise.reject(); + } + + const items = selectedFeedReader.getItems(); + + const filteredItems = search + ? items.filter((item) => { + if (typeof item.title === 'string') { + return item.title.toLowerCase().includes(search.toLowerCase()); + } + return false; + }) + : items; + + return filteredItems.map((item) => { + return { + title: typeof item.title === 'string' ? item.title : 'Unknown', + urls: getTorrentUrlsFromFeedItem(item), + }; + }); + } + + async getPreviouslyMatchedUrls(): Promise> { + return new Promise((resolve, reject) => { + this.db.find({type: 'matchedTorrents'}, (err: Error, docs: Array) => { + if (err) { + reject(err); + return; + } + + resolve(docs.reduce((matchedUrls: Array, doc) => matchedUrls.concat(doc.urls), [])); + }); + }); + } + + async getRules(): Promise> { + return new Promise>((resolve, reject) => { + this.db.find({type: 'rule'}, (err: Error | null, rules: Array) => { + if (err) { + reject(err); + return; + } + + resolve(rules); + }); + }); + } + + handleNewItems = (feedReaderOptions: FeedReaderOptions, feedItems: Array): void => { + this.getPreviouslyMatchedUrls() + .then((previouslyMatchedUrls) => { + const {feedID, feedLabel} = feedReaderOptions; + const applicableRules = this.rules[feedID]; + if (!applicableRules) return; + + const itemsMatchingRules = getFeedItemsMatchingRules(feedItems, applicableRules); + const itemsToDownload = itemsMatchingRules.filter((item) => + item.urls.some((url) => !previouslyMatchedUrls.includes(url)), + ); + + if (itemsToDownload.length === 0) { + return; + } + + Promise.all( + itemsToDownload.map( + async (item): Promise> => { + const {urls, destination, start, tags, ruleID} = item; + await this.services?.clientGatewayService + ?.addTorrentsByURL({ + urls, + cookies: {}, + destination, + tags, + start, + isBasePath: false, + isCompleted: false, + }) + .then(() => { + this.db.update({_id: feedID}, {$inc: {count: 1}}, {upsert: true}); + this.db.update({_id: ruleID}, {$inc: {count: 1}}, {upsert: true}); + }) + .catch(console.error); + + return urls; + }, + ), + ).then((ArrayOfURLArrays) => { + const addedURLs = ArrayOfURLArrays.reduce( + (URLArray: Array, urls: Array) => URLArray.concat(urls), + [], + ); + + this.db.update({type: 'matchedTorrents'}, {$push: {urls: {$each: addedURLs}}}, {upsert: true}); + + this.services?.notificationService.addNotification( + itemsToDownload.map((item) => ({ + id: 'notification.feed.torrent.added', + data: { + title: item.matchTitle, + feedLabel, + ruleLabel: item.ruleLabel, + }, + })), + ); + this.services?.torrentService.fetchTorrentList(); + }); + }) + .catch(console.error); + }; + + private init() { + this.db.find({}, (err: Error, docs: Array) => { + if (err) { + return; + } + + // Create two arrays, one for feeds and one for rules. + const feedsSummary: { + feeds: Array; + rules: Array; + } = docs.reduce( + (accumulator: {feeds: Array; rules: Array}, doc) => { + if (doc.type === 'feed') { + accumulator.feeds.push(doc); + } + + if (doc.type === 'rule') { + accumulator.rules.push(doc); + } + + return accumulator; + }, + {feeds: [], rules: []}, + ); + + // Add all download rules to the local state. + feedsSummary.rules.forEach((rule) => { + const feedID = rule?.feedIDs?.[0]; + + // Migration + if (feedID == null) { + this.removeItem(rule._id).then(() => { + const oldFeedID = ((rule as unknown) as {feedID: string})?.feedID; + if (oldFeedID != null) { + this.addRule({ + destination: rule.destination, + tags: rule.tags, + label: rule.label, + feedIDs: [oldFeedID], + field: rule.field, + match: rule.match, + exclude: rule.exclude, + startOnLoad: rule.startOnLoad, + isBasePath: rule.isBasePath, + }); + } + }); + return; + } + + if (this.rules[feedID] == null) { + this.rules[feedID] = []; + } + + this.rules[feedID].push(rule); + }); + + // Initiate all feeds. + feedsSummary.feeds.forEach((feed) => { + this.startNewFeed(feed); + }); + }); + } + + private loadDatabase(): Datastore { + if (this.db != null) { + return this.db; + } + + const db = new Datastore({ + autoload: true, + filename: path.join(config.dbPath, this.user._id, 'settings', 'feeds.db'), + }); + + return db; + } + + async removeItem(id: string): Promise { + let feedReaderToRemoveIndex = -1; + const feedReaderToRemove = this.feedReaders.find((feedReader, index) => { + if (feedReader.getOptions().feedID === id) { + feedReaderToRemoveIndex = index; + return true; + } + + return false; + }); + + if (feedReaderToRemove != null) { + feedReaderToRemove.stopReader(); + this.feedReaders.splice(feedReaderToRemoveIndex, 1); + } + + return new Promise((resolve, reject) => { + this.db.remove({_id: id}, {}, (err) => { + if (err) { + reject(err); + return; + } + + resolve(); + }); + }); + } + + private startNewFeed(feed: Feed) { + const {_id: feedID, label: feedLabel, url, interval} = feed; + + if (typeof feedID !== 'string' || typeof url !== 'string') { + return false; + } + + if (typeof interval !== 'number') { + return false; + } + + this.feedReaders.push( + new FeedReader({ + feedID, + feedLabel, + url, + interval, + maxHistory: 100, + onNewItems: this.handleNewItems, + }), + ); + + return true; + } +} + +export default FeedService; diff --git a/server/services/historyService.js b/server/services/historyService.js deleted file mode 100644 index 73dfa340c..000000000 --- a/server/services/historyService.js +++ /dev/null @@ -1,212 +0,0 @@ -const BaseService = require('./BaseService'); -const config = require('../../config'); -const HistoryEra = require('../models/HistoryEra'); -const historyServiceEvents = require('../constants/historyServiceEvents'); -const historySnapshotTypes = require('../../shared/constants/historySnapshotTypes'); -const methodCallUtil = require('../util/methodCallUtil'); -const objectUtil = require('../../shared/util/objectUtil'); -const transferSummaryPropMap = require('../constants/transferSummaryPropMap'); - -const transferSummaryMethodCallConfig = methodCallUtil.getMethodCallConfigFromPropMap(transferSummaryPropMap); - -const processData = (opts, callback, data, error) => { - if (error) { - callback(null, error); - return; - } - - data = data.slice(data.length - config.maxHistoryStates); - - callback( - data.reduce( - (accumulator, snapshot) => { - accumulator.download.push(snapshot.dn); - accumulator.upload.push(snapshot.up); - accumulator.timestamps.push(snapshot.ts); - - return accumulator; - }, - {upload: [], download: [], timestamps: []}, - ), - ); -}; - -class HistoryService extends BaseService { - constructor(...serviceConfig) { - super(...serviceConfig); - - this.errorCount = 0; - this.lastSnapshots = {}; - this.pollTimeout = null; - this.transferSummary = {}; - - this.fetchCurrentTransferSummary = this.fetchCurrentTransferSummary.bind(this); - this.handleFetchTransferSummaryError = this.handleFetchTransferSummaryError.bind(this); - this.handleFetchTransferSummarySuccess = this.handleFetchTransferSummarySuccess.bind(this); - - this.yearSnapshot = new HistoryEra(this.user, { - interval: 1000 * 60 * 60 * 24 * 7, // 7 days - maxTime: 0, // infinite - name: 'yearSnapshot', - }); - - this.monthSnapshot = new HistoryEra(this.user, { - interval: 1000 * 60 * 60 * 12, // 12 hours - maxTime: 1000 * 60 * 60 * 24 * 365, // 365 days - name: 'monthSnapshot', - nextEraUpdateInterval: 1000 * 60 * 60 * 24 * 7, // 7 days - nextEra: this.yearSnapshot, - }); - - this.weekSnapshot = new HistoryEra(this.user, { - interval: 1000 * 60 * 60 * 4, // 4 hours - maxTime: 1000 * 60 * 60 * 24 * 7 * 24, // 24 weeks - name: 'weekSnapshot', - nextEraUpdateInterval: 1000 * 60 * 60 * 12, // 12 hours - nextEra: this.monthSnapshot, - }); - - this.daySnapshot = new HistoryEra(this.user, { - interval: 1000 * 60 * 60, // 60 minutes - maxTime: 1000 * 60 * 60 * 24 * 30, // 30 days - name: 'daySnapshot', - nextEraUpdateInterval: 1000 * 60 * 60 * 4, // 4 hours - nextEra: this.weekSnapshot, - }); - - this.hourSnapshot = new HistoryEra(this.user, { - interval: 1000 * 60 * 15, // 15 minutes - maxTime: 1000 * 60 * 60 * 24, // 24 hours - name: 'hourSnapshot', - nextEraUpdateInterval: 1000 * 60 * 60, // 60 minutes - nextEra: this.daySnapshot, - }); - - this.thirtyMinSnapshot = new HistoryEra(this.user, { - interval: 1000 * 20, // 20 seconds - maxTime: 1000 * 60 * 30, // 30 minutes - name: 'thirtyMinSnapshot', - nextEraUpdateInterval: 1000 * 60 * 15, // 15 minutes - nextEra: this.hourSnapshot, - }); - - this.fiveMinSnapshot = new HistoryEra(this.user, { - interval: 1000 * 5, // 5 seconds - maxTime: 1000 * 60 * 5, // 5 minutes - name: 'fiveMinSnapshot', - nextEraUpdateInterval: 1000 * 20, // 20 seconds - nextEra: this.thirtyMinSnapshot, - }); - - this.fetchCurrentTransferSummary(); - } - - checkSnapshotDiffs() { - Object.keys(historySnapshotTypes).forEach(snapshotType => { - this.getHistory({snapshot: historySnapshotTypes[snapshotType]}, (nextSnapshot, error) => { - if (error) { - return; - } - - const lastSnapshot = this.lastSnapshots[snapshotType] || {}; - const {timestamps = []} = lastSnapshot; - const nextLastTimestamp = timestamps[timestamps.length - 1]; - const prevLastTimestamp = nextSnapshot.timestamps[nextSnapshot.timestamps.length - 1]; - - if (nextLastTimestamp !== prevLastTimestamp) { - this.emit(historyServiceEvents[`${snapshotType}_SNAPSHOT_FULL_UPDATE`], { - id: nextLastTimestamp, - data: nextSnapshot, - }); - } - - this.lastSnapshots[snapshotType] = nextSnapshot; - }); - }); - } - - deferFetchTransferSummary(interval = config.torrentClientPollInterval || 2000) { - this.pollTimeout = setTimeout(this.fetchCurrentTransferSummary, interval); - } - - destroy() { - clearTimeout(this.pollTimeout); - } - - fetchCurrentTransferSummary() { - if (this.pollTimeout != null) { - clearTimeout(this.pollTimeout); - } - - this.services.clientGatewayService - .fetchTransferSummary(transferSummaryMethodCallConfig) - .then(this.handleFetchTransferSummarySuccess.bind(this)) - .catch(this.handleFetchTransferSummaryError.bind(this)); - } - - getTransferSummary() { - return { - id: Date.now(), - transferSummary: this.transferSummary, - }; - } - - getHistory(opts = {}, callback) { - const historyCallback = processData.bind(this, opts, callback); - - if (opts.snapshot === historySnapshotTypes.FIVE_MINUTE) { - this.fiveMinSnapshot.getData(opts, historyCallback); - } else if (opts.snapshot === historySnapshotTypes.THIRTY_MINUTE) { - this.thirtyMinSnapshot.getData(opts, historyCallback); - } else if (opts.snapshot === historySnapshotTypes.HOUR) { - this.hourSnapshot.getData(opts, historyCallback); - } else if (opts.snapshot === historySnapshotTypes.DAY) { - this.daySnapshot.getData(opts, historyCallback); - } else if (opts.snapshot === historySnapshotTypes.WEEK) { - this.weekSnapshot.getData(opts, historyCallback); - } else if (opts.snapshot === historySnapshotTypes.MONTH) { - this.monthSnapshot.getData(opts, historyCallback); - } else if (opts.snapshot === historySnapshotTypes.YEAR) { - this.yearSnapshot.getData(opts, historyCallback); - } - } - - handleFetchTransferSummarySuccess(nextTransferSummary) { - const summaryDiff = objectUtil.getDiff(this.transferSummary, nextTransferSummary); - - if (summaryDiff.length > 0) { - this.emit(historyServiceEvents.TRANSFER_SUMMARY_DIFF_CHANGE, { - diff: summaryDiff, - id: Date.now(), - }); - } - - this.errorCount = 0; - this.transferSummary = nextTransferSummary; - this.fiveMinSnapshot.addData({ - upload: nextTransferSummary.upRate, - download: nextTransferSummary.downRate, - }); - - this.checkSnapshotDiffs(); - this.deferFetchTransferSummary(); - - this.emit(historyServiceEvents.FETCH_TRANSFER_SUMMARY_SUCCESS); - } - - handleFetchTransferSummaryError() { - let nextInterval = config.torrentClientPollInterval || 2000; - - // If more than consecutive errors have occurred, then we delay the next - // request. - if (++this.errorCount >= 3) { - nextInterval = Math.max(nextInterval + (this.errorCount * nextInterval) / 4, 1000 * 60); - } - - this.deferFetchTransferSummary(nextInterval); - - this.emit(historyServiceEvents.FETCH_TRANSFER_SUMMARY_ERROR); - } -} - -module.exports = HistoryService; diff --git a/server/services/historyService.ts b/server/services/historyService.ts new file mode 100644 index 000000000..062218d78 --- /dev/null +++ b/server/services/historyService.ts @@ -0,0 +1,177 @@ +import jsonpatch, {Operation} from 'fast-json-patch'; + +import type {HistorySnapshot} from '@shared/constants/historySnapshotTypes'; +import type {TransferHistory, TransferSummary} from '@shared/types/TransferData'; + +import BaseService from './BaseService'; +import config from '../../config'; +import HistoryEra from '../models/HistoryEra'; + +interface HistoryServiceEvents { + TRANSFER_SUMMARY_DIFF_CHANGE: (payload: {id: number; diff: Operation[]}) => void; + FETCH_TRANSFER_SUMMARY_SUCCESS: () => void; + FETCH_TRANSFER_SUMMARY_ERROR: () => void; +} + +class HistoryService extends BaseService { + errorCount = 0; + pollTimeout?: NodeJS.Timeout; + lastSnapshots: Partial> = {}; + + transferSummary: TransferSummary = { + downRate: 0, + downTotal: 0, + upRate: 0, + upTotal: 0, + }; + + snapshots: Record = { + YEAR: new HistoryEra(this.user, { + interval: 1000 * 60 * 60 * 24 * 7, // 7 days + maxTime: 0, // infinite + name: 'yearSnapshot', + }), + + MONTH: new HistoryEra(this.user, { + interval: 1000 * 60 * 60 * 12, // 12 hours + maxTime: 1000 * 60 * 60 * 24 * 365, // 365 days + name: 'monthSnapshot', + nextEraUpdateInterval: 1000 * 60 * 60 * 24 * 7, // 7 days + }), + + WEEK: new HistoryEra(this.user, { + interval: 1000 * 60 * 60 * 4, // 4 hours + maxTime: 1000 * 60 * 60 * 24 * 7 * 24, // 24 weeks + name: 'weekSnapshot', + nextEraUpdateInterval: 1000 * 60 * 60 * 12, // 12 hours + }), + + DAY: new HistoryEra(this.user, { + interval: 1000 * 60 * 60, // 60 minutes + maxTime: 1000 * 60 * 60 * 24 * 30, // 30 days + name: 'daySnapshot', + nextEraUpdateInterval: 1000 * 60 * 60 * 4, // 4 hours + }), + + HOUR: new HistoryEra(this.user, { + interval: 1000 * 60 * 15, // 15 minutes + maxTime: 1000 * 60 * 60 * 24, // 24 hours + name: 'hourSnapshot', + nextEraUpdateInterval: 1000 * 60 * 60, // 60 minutes + }), + + THIRTY_MINUTE: new HistoryEra(this.user, { + interval: 1000 * 20, // 20 seconds + maxTime: 1000 * 60 * 30, // 30 minutes + name: 'thirtyMinSnapshot', + nextEraUpdateInterval: 1000 * 60 * 15, // 15 minutes + }), + + FIVE_MINUTE: new HistoryEra(this.user, { + interval: 1000 * 5, // 5 seconds + maxTime: 1000 * 60 * 5, // 5 minutes + name: 'fiveMinSnapshot', + nextEraUpdateInterval: 1000 * 20, // 20 seconds + }), + } as const; + + constructor(...args: ConstructorParameters) { + super(...args); + + let nextEra: HistoryEra; + Object.values(this.snapshots).forEach((snapshot, index) => { + if (index === 0) { + nextEra = snapshot; + return; + } + snapshot.setNextEra(nextEra); + nextEra = snapshot; + }); + + this.onServicesUpdated = () => { + this.fetchCurrentTransferSummary(); + }; + } + + private fetchCurrentTransferSummary = () => { + if (this.pollTimeout != null) { + clearTimeout(this.pollTimeout); + } + + this.services?.clientGatewayService + ?.fetchTransferSummary() + .then(this.handleFetchTransferSummarySuccess) + .catch(this.handleFetchTransferSummaryError); + }; + + private deferFetchTransferSummary(interval = config.torrentClientPollInterval) { + this.pollTimeout = setTimeout(this.fetchCurrentTransferSummary, interval); + } + + private handleFetchTransferSummarySuccess = async (nextTransferSummary: TransferSummary): Promise => { + const summaryDiff = jsonpatch.compare(this.transferSummary, nextTransferSummary); + + this.emit('TRANSFER_SUMMARY_DIFF_CHANGE', { + diff: summaryDiff, + id: Date.now(), + }); + + this.errorCount = 0; + this.transferSummary = nextTransferSummary; + + await this.snapshots.FIVE_MINUTE.addData({ + upload: nextTransferSummary.upRate, + download: nextTransferSummary.downRate, + }); + + this.deferFetchTransferSummary(); + + this.emit('FETCH_TRANSFER_SUMMARY_SUCCESS'); + }; + + private handleFetchTransferSummaryError = () => { + let nextInterval = config.torrentClientPollInterval; + + // If more than 2 consecutive errors have occurred, then we delay the next request. + this.errorCount += 1; + if (this.errorCount > 2) { + nextInterval = Math.max(nextInterval + (this.errorCount * nextInterval) / 4, 1000 * 60); + } + + this.deferFetchTransferSummary(nextInterval); + + this.emit('FETCH_TRANSFER_SUMMARY_ERROR'); + }; + + destroy() { + if (this.pollTimeout != null) { + clearTimeout(this.pollTimeout); + } + + super.destroy(); + } + + getTransferSummary() { + return { + id: Date.now(), + transferSummary: this.transferSummary, + } as const; + } + + async getHistory({snapshot}: {snapshot: HistorySnapshot}): Promise { + return this.snapshots[snapshot]?.getData().then((transferSnapshots) => + transferSnapshots.reduce( + (history, transferSnapshot) => { + history.download.push(transferSnapshot.download); + history.upload.push(transferSnapshot.upload); + history.timestamps.push(transferSnapshot.timestamp); + + return history; + }, + {upload: [], download: [], timestamps: []} as TransferHistory, + ), + ); + } +} + +export default HistoryService; diff --git a/server/services/index.js b/server/services/index.js deleted file mode 100644 index f7d791533..000000000 --- a/server/services/index.js +++ /dev/null @@ -1,127 +0,0 @@ -const ClientGatewayService = require('./clientGatewayService'); -const ClientRequestManager = require('./clientRequestManager'); -const FeedService = require('./feedService'); -const HistoryService = require('./historyService'); -const NotificationService = require('./notificationService'); -const TaxonomyService = require('./taxonomyService'); -const TorrentService = require('./torrentService'); - -const clientRequestManagers = new Map(); -const clientGatewayServices = new Map(); -const feedServices = new Map(); -const historyServices = new Map(); -const notificationServices = new Map(); -const taxonomyServices = new Map(); -const torrentServices = new Map(); -const allServiceMaps = [ - clientRequestManagers, - clientGatewayServices, - feedServices, - historyServices, - notificationServices, - taxonomyServices, - torrentServices, -]; - -const getService = ({servicesMap, service: Service, user}) => { - let serviceInstance = servicesMap.get(user._id); - if (!serviceInstance) { - // eslint-disable-next-line no-use-before-define - serviceInstance = new Service(user, getAllServices(user)); - servicesMap.set(user._id, serviceInstance); - } - - return serviceInstance; -}; - -const getClientRequestManager = user => - getService({servicesMap: clientRequestManagers, service: ClientRequestManager, user}); - -const getClientGatewayService = user => - getService({servicesMap: clientGatewayServices, service: ClientGatewayService, user}); - -const getFeedService = user => getService({servicesMap: feedServices, service: FeedService, user}); - -const getHistoryService = user => getService({servicesMap: historyServices, service: HistoryService, user}); - -const getNotificationService = user => - getService({servicesMap: notificationServices, service: NotificationService, user}); - -const getTaxonomyService = user => getService({servicesMap: taxonomyServices, service: TaxonomyService, user}); - -const getTorrentService = user => getService({servicesMap: torrentServices, service: TorrentService, user}); - -const bootstrapServicesForUser = user => { - getClientRequestManager(user); - getClientGatewayService(user); - getFeedService(user); - getHistoryService(user); - getNotificationService(user); - getTaxonomyService(user); - getTorrentService(user); -}; - -const destroyUserServices = user => { - const userId = user._id; - allServiceMaps.forEach(serviceMap => { - const userService = serviceMap.get(userId); - if (userService != null) { - userService.destroy(); - serviceMap.delete(userId); - } - }); -}; - -const getAllServices = user => ({ - get clientRequestManager() { - return getClientRequestManager(user); - }, - - get clientGatewayService() { - return getClientGatewayService(user); - }, - - get feedService() { - return getFeedService(user); - }, - - get historyService() { - return getHistoryService(user); - }, - - get notificationService() { - return getNotificationService(user); - }, - - get taxonomyService() { - return getTaxonomyService(user); - }, - - get torrentService() { - return getTorrentService(user); - }, -}); - -const updateUserServices = user => { - const userId = user._id; - allServiceMaps.forEach(serviceMap => { - const service = serviceMap.get(userId); - if (service != null) { - service.updateUser(user); - serviceMap.delete(userId); - } - }); -}; - -module.exports = { - bootstrapServicesForUser, - destroyUserServices, - getAllServices, - getClientRequestManager, - getClientGatewayService, - getHistoryService, - getNotificationService, - getTaxonomyService, - getTorrentService, - updateUserServices, -}; diff --git a/server/services/index.ts b/server/services/index.ts new file mode 100644 index 000000000..0267781a7 --- /dev/null +++ b/server/services/index.ts @@ -0,0 +1,183 @@ +import type {UserInDatabase} from '@shared/schema/Auth'; + +import BaseService from './BaseService'; +import ClientGatewayService from './interfaces/clientGatewayService'; +import FeedService from './feedService'; +import HistoryService from './historyService'; +import NotificationService from './notificationService'; +import SettingService from './settingService'; +import TaxonomyService from './taxonomyService'; +import TorrentService from './torrentService'; + +import QBittorrentClientGatewayService from './qBittorrent/clientGatewayService'; +import RTorrentClientGatewayService from './rTorrent/clientGatewayService'; +import TransmissionClientGatewayService from './Transmission/clientGatewayService'; + +type ClientGatewayServiceImpl = typeof ClientGatewayService & { + new (...args: ConstructorParameters): + | QBittorrentClientGatewayService + | RTorrentClientGatewayService + | TransmissionClientGatewayService; +}; + +type Service = + | ClientGatewayServiceImpl + | typeof FeedService + | typeof HistoryService + | typeof NotificationService + | typeof SettingService + | typeof TaxonomyService + | typeof TorrentService; + +const serviceInstances: { + clientGatewayServices: Record; + feedServices: Record; + historyServices: Record; + notificationServices: Record; + settingServices: Record; + taxonomyServices: Record; + torrentServices: Record; +} = { + clientGatewayServices: {}, + feedServices: {}, + historyServices: {}, + notificationServices: {}, + settingServices: {}, + taxonomyServices: {}, + torrentServices: {}, +}; + +type ServiceMap = keyof typeof serviceInstances; + +const getService = (servicesMap: ServiceMap, Service: S, user: UserInDatabase): InstanceType => { + // if a service instance for user exists, return it + const serviceInstance = serviceInstances[servicesMap][user._id]; + if (serviceInstance != null) { + return serviceInstance as InstanceType; + } + + // otherwise, create a new service instance and return it + const newInstance = new Service(user) as InstanceType; + serviceInstances[servicesMap][user._id] = newInstance; + return newInstance; +}; + +const getClientGatewayService = (user: UserInDatabase): ClientGatewayService | undefined => { + switch (user.client.client) { + case 'qBittorrent': + return getService('clientGatewayServices', QBittorrentClientGatewayService, user); + case 'rTorrent': + return getService('clientGatewayServices', RTorrentClientGatewayService, user); + case 'Transmission': + return getService('clientGatewayServices', TransmissionClientGatewayService, user); + default: + return undefined; + } +}; + +const getFeedService = (user: UserInDatabase): FeedService => { + return getService('feedServices', FeedService, user); +}; + +const getHistoryService = (user: UserInDatabase): HistoryService => { + return getService('historyServices', HistoryService, user); +}; + +const getNotificationService = (user: UserInDatabase): NotificationService => { + return getService('notificationServices', NotificationService, user); +}; + +const getSettingService = (user: UserInDatabase): SettingService => { + return getService('settingServices', SettingService, user); +}; + +const getTaxonomyService = (user: UserInDatabase): TaxonomyService => { + return getService('taxonomyServices', TaxonomyService, user); +}; + +const getTorrentService = (user: UserInDatabase): TorrentService => { + return getService('torrentServices', TorrentService, user); +}; + +const getAllServices = (user: UserInDatabase) => + ({ + get clientGatewayService() { + return getClientGatewayService(user); + }, + + get feedService() { + return getFeedService(user); + }, + + get historyService() { + return getHistoryService(user); + }, + + get notificationService() { + return getNotificationService(user); + }, + + get settingService() { + return getSettingService(user); + }, + + get taxonomyService() { + return getTaxonomyService(user); + }, + + get torrentService() { + return getTorrentService(user); + }, + } as const); + +const createUserServices = (user: UserInDatabase): boolean => { + return !Object.values(getAllServices(user)).some((service) => { + if (service == null) { + return true; + } + return false; + }); +}; + +const destroyUserServices = (userId: UserInDatabase['_id']) => { + Object.keys(serviceInstances).forEach((key) => { + const serviceMap = key as keyof typeof serviceInstances; + const userService = serviceInstances[serviceMap][userId]; + if (userService != null) { + delete serviceInstances[serviceMap][userId]; + userService.destroy(); + } + }); +}; + +const linkUserServices = (user: UserInDatabase) => { + Object.keys(serviceInstances).forEach((key) => { + const serviceMap = key as ServiceMap; + const service = serviceInstances[serviceMap][user._id]; + if (service != null) { + service.updateServices(getAllServices(user)); + } + }); +}; + +const bootstrapServicesForUser = (user: UserInDatabase) => { + if (createUserServices(user) === false) { + console.error(`Failed to initialize services for user ${user.username}`); + return; + } + linkUserServices(user); +}; + +export type UserServices = ReturnType; + +export default { + bootstrapServicesForUser, + destroyUserServices, + getAllServices, + getClientGatewayService, + getHistoryService, + getNotificationService, + getSettingService, + getTaxonomyService, + getTorrentService, +}; diff --git a/server/services/interfaces/clientGatewayService.ts b/server/services/interfaces/clientGatewayService.ts new file mode 100644 index 000000000..fbc341726 --- /dev/null +++ b/server/services/interfaces/clientGatewayService.ts @@ -0,0 +1,227 @@ +import type { + AddTorrentByFileOptions, + AddTorrentByURLOptions, + SetTorrentsTagsOptions, +} from '@shared/schema/api/torrents'; +import type { + CheckTorrentsOptions, + DeleteTorrentsOptions, + MoveTorrentsOptions, + SetTorrentContentsPropertiesOptions, + SetTorrentsPriorityOptions, + SetTorrentsTrackersOptions, + StartTorrentsOptions, + StopTorrentsOptions, +} from '@shared/types/api/torrents'; +import type {ClientSettings} from '@shared/types/ClientSettings'; +import type {SetClientSettingsOptions} from '@shared/types/api/client'; +import type {TorrentContent} from '@shared/types/TorrentContent'; +import type {TorrentListSummary, TorrentProperties} from '@shared/types/Torrent'; +import type {TorrentPeer} from '@shared/types/TorrentPeer'; +import type {TorrentTracker} from '@shared/types/TorrentTracker'; +import type {TransferSummary} from '@shared/types/TransferData'; + +import BaseService from '../BaseService'; +import config from '../../../config'; + +interface ClientGatewayServiceEvents { + CLIENT_CONNECTION_STATE_CHANGE: (isConnected: boolean) => void; + PROCESS_TORRENT_LIST_START: () => void; + PROCESS_TORRENT_LIST_END: (torrentListSummary: TorrentListSummary) => void; + PROCESS_TORRENT: (torrentProperties: TorrentProperties) => void; +} + +abstract class ClientGatewayService extends BaseService { + errorCount = 0; + retryTimer: NodeJS.Timeout | null = null; + + /** + * Adds torrents by file + * + * @param {Required} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract addTorrentsByFile(options: Required): Promise; + + /** + * Adds torrents by URL + * + * @param {Required} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract addTorrentsByURL(options: Required): Promise; + + /** + * Checks torrents + * + * @param {CheckTorrentsOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract checkTorrents({hashes}: CheckTorrentsOptions): Promise; + + /** + * Gets the list of contents of a torrent. + * + * @param {string} hash - Hash of torrent + * @return {Promise} - Resolves with TorrentContentTree or rejects with error. + */ + abstract getTorrentContents(hash: TorrentProperties['hash']): Promise>; + + /** + * Gets the list of peers of a torrent. + * + * @param {string} hash - Hash of torrent + * @return {Promise>} - Resolves with an array of TorrentPeer or rejects with error. + */ + abstract getTorrentPeers(hash: TorrentProperties['hash']): Promise>; + + /** + * Gets the list of trackers of a torrent. + * + * @param {string} hash - Hash of torrent + * @return {Promise>} - Resolves with an array of TorrentTracker or rejects with error. + */ + abstract getTorrentTrackers(hash: TorrentProperties['hash']): Promise>; + + /** + * Moves torrents to specified destination path. + * + * @param {MoveTorrentsOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract moveTorrents(options: MoveTorrentsOptions): Promise; + + /** + * Removes torrents. Optionally deletes data of torrents. + * + * @param {DeleteTorrentsOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract removeTorrents(options: DeleteTorrentsOptions): Promise; + + /** + * Sets priority of torrents + * + * @param {SetTorrentsPriorityOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract setTorrentsPriority(options: SetTorrentsPriorityOptions): Promise; + + /** + * Sets tags of torrents + * + * @param {SetTorrentsTagsOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract setTorrentsTags(options: SetTorrentsTagsOptions): Promise; + + /** + * Sets trackers of torrents + * + * @param {SetTorrentsTrackersOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract setTorrentsTrackers(options: SetTorrentsTrackersOptions): Promise; + + /** + * Sets priority of contents of a torrent + * + * @param {string} hash - Hash of the torrent. + * @param {SetTorrentContentsPropertiesOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract setTorrentContentsPriority(hash: string, options: SetTorrentContentsPropertiesOptions): Promise; + + /** + * Starts torrents + * + * @param {StartTorrentsOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract startTorrents(options: StartTorrentsOptions): Promise; + + /** + * Stops torrents + * + * @param {StopTorrentsOptions} options - An object of options... + * @return {Promise} - Rejects with error. + */ + abstract stopTorrents(options: StopTorrentsOptions): Promise; + + /** + * Fetches the list of torrents + * + * @return {Promise} - Resolves with TorrentListSummary or rejects with error. + */ + abstract fetchTorrentList(): Promise; + + /** + * Fetches the transfer summary + * + * @return {Promise} - Resolves with TransferSummary or rejects with error. + */ + abstract fetchTransferSummary(): Promise; + + /** + * Gets settings of the torrent client + * + * @return {Promise} - Resolves with ClientSettings or rejects with error. + */ + abstract getClientSettings(): Promise; + + /** + * Sets settings of the torrent client + * + * @param {SetClientSettingsOptions} - Settings to be set. + * @return {Promise} - Rejects with error. + */ + abstract setClientSettings(settings: SetClientSettingsOptions): Promise; + + abstract testGateway(): Promise; + + destroyTimer() { + if (this.retryTimer != null) { + clearTimeout(this.retryTimer); + this.retryTimer = null; + } + } + + destroy() { + this.destroyTimer(); + super.destroy(); + } + + startTimer() { + if (this.retryTimer == null) { + this.retryTimer = setTimeout(() => { + this.errorCount += 1; + this.destroyTimer(); + this.testGateway().catch(() => undefined); + }, config.torrentClientPollInterval * this.errorCount); + } + } + + processClientRequestSuccess = (response: T): T => { + this.destroyTimer(); + + if (this.errorCount !== 0) { + this.errorCount = 0; + this.emit('CLIENT_CONNECTION_STATE_CHANGE', true); + } + + return response; + }; + + processClientRequestError = (error: Error) => { + if (this.errorCount === 0) { + this.errorCount += 1; + this.emit('CLIENT_CONNECTION_STATE_CHANGE', false); + } + + this.startTimer(); + + throw error; + }; +} + +export default ClientGatewayService; diff --git a/server/services/notificationService.js b/server/services/notificationService.js deleted file mode 100644 index f3e054eb8..000000000 --- a/server/services/notificationService.js +++ /dev/null @@ -1,125 +0,0 @@ -const _ = require('lodash'); -const Datastore = require('nedb'); -const path = require('path'); - -const BaseService = require('./BaseService'); -const config = require('../../config'); -const notificationServiceEvents = require('../constants/notificationServiceEvents'); - -const DEFAULT_QUERY_LIMIT = 20; -const INITIAL_COUNT_VALUE = {read: 0, total: 0, unread: 0}; - -class NotificationService extends BaseService { - constructor(...serviceConfig) { - super(...serviceConfig); - - this.count = Object.assign({}, INITIAL_COUNT_VALUE); - this.ready = false; - - this.db = this.loadDatabase(); - - this.emitUpdate = _.debounce(this.emitUpdate.bind(this), 100); - this.countNotifications(); - } - - addNotification(notifications) { - notifications = _.castArray(notifications); - - this.count.total = this.count.total + notifications.length; - this.count.unread = this.count.unread + notifications.length; - - const timestamp = Date.now(); - const notificationsToInsert = notifications.map(notification => ({ - ts: timestamp, - data: notification.data, - id: notification.id, - read: false, - })); - - this.db.insert(notificationsToInsert, () => this.emitUpdate()); - } - - clearNotifications(options, callback) { - this.db.remove({}, {multi: true}, err => { - if (err) { - callback(null, err); - return; - } - - this.count = Object.assign({}, INITIAL_COUNT_VALUE); - - callback(); - }); - - this.emitUpdate(); - } - - countNotifications() { - this.db.find({}, (err, docs) => { - if (err) { - this.count = Object.assign({}, INITIAL_COUNT_VALUE); - } else { - docs.forEach(notification => { - if (notification.read) { - this.count.read++; - } else { - this.count.unread++; - } - - this.count.total++; - }); - } - - this.emitUpdate(); - }); - } - - emitUpdate() { - this.emit(notificationServiceEvents.NOTIFICATION_COUNT_CHANGE, { - id: Date.now(), - data: this.count, - }); - } - - getNotificationCount() { - return this.count; - } - - getNotifications(query, callback) { - const sortedNotifications = this.db.find({}).sort({ts: -1}); - const queryCallback = (err, docs) => { - if (err) { - callback(null, err); - return; - } - - callback({notifications: docs, count: this.count}); - }; - - if (query.allNotifications) { - sortedNotifications.exec(queryCallback); - } else if (query.start != null) { - sortedNotifications - .skip(Number(query.start)) - .limit(Number(query.limit) || DEFAULT_QUERY_LIMIT) - .exec(queryCallback); - } else { - sortedNotifications.limit(Number(query.limit) || DEFAULT_QUERY_LIMIT).exec(queryCallback); - } - } - - loadDatabase() { - if (this.ready) return; - - const db = new Datastore({ - autoload: true, - filename: path.join(config.dbPath, this.user._id, 'notifications.db'), - }); - - this.ready = true; - - return db; - } -} - -module.exports = NotificationService; diff --git a/server/services/notificationService.ts b/server/services/notificationService.ts new file mode 100644 index 000000000..3370a8906 --- /dev/null +++ b/server/services/notificationService.ts @@ -0,0 +1,133 @@ +import Datastore from 'nedb'; +import path from 'path'; + +import type {Notification, NotificationCount, NotificationFetchOptions} from '@shared/types/Notification'; + +import BaseService from './BaseService'; +import config from '../../config'; + +interface NotificationServiceEvents { + NOTIFICATION_COUNT_CHANGE: (payload: {id: number; data: NotificationCount}) => void; +} + +const DEFAULT_QUERY_LIMIT = 20; + +class NotificationService extends BaseService { + count: NotificationCount = {read: 0, total: 0, unread: 0}; + db = this.loadDatabase(); + + constructor(...args: ConstructorParameters) { + super(...args); + + this.onServicesUpdated = () => { + this.countNotifications(); + }; + } + + addNotification(notifications: Array>) { + this.count.total += notifications.length; + this.count.unread += notifications.length; + + const timestamp = Date.now(); + const notificationsToInsert = notifications.map((notification) => ({ + ts: timestamp, + data: notification.data, + id: notification.id, + read: false, + })) as Notification[]; + + this.db.insert(notificationsToInsert, () => this.emitUpdate()); + } + + clearNotifications(callback: (data?: null, err?: Error) => void) { + this.db.remove({}, {multi: true}, (err) => { + if (err) { + callback(null, err); + return; + } + + this.count = {read: 0, total: 0, unread: 0}; + this.emitUpdate(); + + callback(); + }); + } + + countNotifications() { + this.db.find({}, (err: Error, notifications: Array) => { + if (err) { + this.count = {read: 0, total: 0, unread: 0}; + } else { + notifications.forEach((notification) => { + if (notification.read) { + this.count.read += 1; + } else { + this.count.unread += 1; + } + + this.count.total += 1; + }); + } + + this.emitUpdate(); + }); + } + + emitUpdate = () => { + this.emit('NOTIFICATION_COUNT_CHANGE', { + id: Date.now(), + data: this.count, + }); + }; + + getNotificationCount() { + return this.count; + } + + getNotifications( + query: NotificationFetchOptions, + callback: ( + data: { + notifications: Notification[][]; + count: NotificationCount; + } | null, + err?: Error, + ) => void, + ) { + const sortedNotifications = this.db.find({}).sort({ts: -1}); + const queryCallback = (err: Error | null, notifications: Notification[][]) => { + if (err) { + callback(null, err); + return; + } + + callback({notifications, count: this.count}); + }; + + if (query.allNotifications) { + sortedNotifications.exec(queryCallback); + } else if (query.start != null) { + sortedNotifications + .skip(Number(query.start)) + .limit(Number(query.limit) || DEFAULT_QUERY_LIMIT) + .exec(queryCallback); + } else { + sortedNotifications.limit(Number(query.limit) || DEFAULT_QUERY_LIMIT).exec(queryCallback); + } + } + + loadDatabase(): Datastore> { + if (this.db != null) { + return this.db; + } + + const db = new Datastore({ + autoload: true, + filename: path.join(config.dbPath, this.user._id, 'notifications.db'), + }); + + return db; + } +} + +export default NotificationService; diff --git a/server/services/qBittorrent/clientGatewayService.ts b/server/services/qBittorrent/clientGatewayService.ts new file mode 100644 index 000000000..8ca9960ef --- /dev/null +++ b/server/services/qBittorrent/clientGatewayService.ts @@ -0,0 +1,405 @@ +import type { + AddTorrentByFileOptions, + AddTorrentByURLOptions, + SetTorrentsTagsOptions, +} from '@shared/schema/api/torrents'; +import type { + CheckTorrentsOptions, + DeleteTorrentsOptions, + MoveTorrentsOptions, + SetTorrentContentsPropertiesOptions, + SetTorrentsPriorityOptions, + SetTorrentsTrackersOptions, + StartTorrentsOptions, + StopTorrentsOptions, +} from '@shared/types/api/torrents'; +import type {ClientSettings} from '@shared/types/ClientSettings'; +import type {QBittorrentConnectionSettings} from '@shared/schema/ClientConnectionSettings'; +import type {TorrentContent} from '@shared/types/TorrentContent'; +import type {TorrentList, TorrentListSummary, TorrentProperties} from '@shared/types/Torrent'; +import type {TorrentPeer} from '@shared/types/TorrentPeer'; +import type {TorrentTracker} from '@shared/types/TorrentTracker'; +import type {TransferSummary} from '@shared/types/TransferData'; +import type {SetClientSettingsOptions} from '@shared/types/api/client'; + +import ClientGatewayService from '../interfaces/clientGatewayService'; +import ClientRequestManager from './clientRequestManager'; +import {getDomainsFromURLs} from '../../util/torrentPropertiesUtil'; +import { + getTorrentPeerPropertiesFromFlags, + getTorrentStatusFromState, + getTorrentTrackerTypeFromURL, +} from './util/torrentPropertiesUtil'; +import {QBittorrentTorrentContentPriority, QBittorrentTorrentTrackerStatus} from './types/QBittorrentTorrentsMethods'; +import {TorrentContentPriority} from '../../../shared/types/TorrentContent'; +import {TorrentPriority} from '../../../shared/types/Torrent'; +import {TorrentTrackerType} from '../../../shared/types/TorrentTracker'; + +class QBittorrentClientGatewayService extends ClientGatewayService { + clientRequestManager = new ClientRequestManager(this.user.client as QBittorrentConnectionSettings); + cachedProperties: Record> = {}; + + async addTorrentsByFile({ + files, + destination, + tags, + isBasePath, + start, + }: Required): Promise { + const fileBuffers = files.map((file) => { + return Buffer.from(file, 'base64'); + }); + + return this.clientRequestManager + .torrentsAddFiles(fileBuffers, { + savepath: destination, + tags: tags.join(','), + paused: !start, + root_folder: !isBasePath, + }) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async addTorrentsByURL({ + urls, + cookies, + destination, + tags, + isBasePath, + start, + }: Required): Promise { + return this.clientRequestManager + .torrentsAddURLs(urls, { + cookie: cookies != null ? Object.values(cookies)[0]?.[0] : undefined, + savepath: destination, + tags: tags.join(','), + paused: !start, + root_folder: !isBasePath, + }) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async checkTorrents({hashes}: CheckTorrentsOptions): Promise { + return this.clientRequestManager + .torrentsRecheck(hashes) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async getTorrentContents(hash: TorrentProperties['hash']): Promise> { + return this.clientRequestManager + .getTorrentContents(hash) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((contents) => { + return contents.map((content, index) => { + let priority = TorrentContentPriority.NORMAL; + + switch (content.priority) { + case QBittorrentTorrentContentPriority.DO_NOT_DOWNLOAD: + priority = TorrentContentPriority.DO_NOT_DOWNLOAD; + break; + case QBittorrentTorrentContentPriority.HIGH: + case QBittorrentTorrentContentPriority.MAXIMUM: + priority = TorrentContentPriority.HIGH; + break; + default: + break; + } + + return { + index, + path: content.name, + filename: content.name.split('/').pop() || '', + percentComplete: content.progress * 100, + priority, + sizeBytes: content.size, + }; + }); + }); + } + + async getTorrentPeers(hash: TorrentProperties['hash']): Promise> { + return this.clientRequestManager + .syncTorrentPeers(hash) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((peers) => { + return Object.keys(peers).reduce((accumulator: Array, ip_and_port) => { + const peer = peers[ip_and_port]; + + // Only displays connected peers + if (!peer.flags.includes('D') && !peer.flags.includes('U')) { + return accumulator; + } + + const properties = getTorrentPeerPropertiesFromFlags(peer.flags); + accumulator.push({ + address: peer.ip, + country: peer.country_code, + clientVersion: peer.client, + completedPercent: peer.progress * 100, + downloadRate: peer.dl_speed, + uploadRate: peer.up_speed, + isEncrypted: properties.isEncrypted, + isIncoming: properties.isIncoming, + }); + + return accumulator; + }, []); + }); + } + + async getTorrentTrackers(hash: TorrentProperties['hash']): Promise> { + return this.clientRequestManager + .getTorrentTrackers(hash) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((trackers) => { + return trackers + .filter((tracker) => tracker.status !== QBittorrentTorrentTrackerStatus.DISABLED) + .map((tracker) => { + return { + url: tracker.url, + type: getTorrentTrackerTypeFromURL(tracker.url), + }; + }); + }); + } + + async moveTorrents({hashes, destination}: MoveTorrentsOptions): Promise { + return this.clientRequestManager + .torrentsSetLocation(hashes, destination) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async removeTorrents({hashes, deleteData}: DeleteTorrentsOptions): Promise { + return this.clientRequestManager + .torrentsDelete(hashes, deleteData || false) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async setTorrentsPriority({hashes, priority}: SetTorrentsPriorityOptions): Promise { + // TODO: qBittorrent uses queue and priority here has a different meaning + switch (priority) { + case TorrentPriority.DO_NOT_DOWNLOAD: + return this.stopTorrents({hashes}); + case TorrentPriority.LOW: + return this.clientRequestManager + .torrentsSetBottomPrio(hashes) + .then(this.processClientRequestSuccess, this.processClientRequestError); + case TorrentPriority.HIGH: + return this.clientRequestManager + .torrentsSetTopPrio(hashes) + .then(this.processClientRequestSuccess, this.processClientRequestError); + default: + return undefined; + } + } + + async setTorrentsTags({hashes, tags}: SetTorrentsTagsOptions): Promise { + return this.clientRequestManager.torrentsRemoveTags(hashes).then(() => { + this.clientRequestManager + .torrentsAddTags(hashes, tags) + .then(this.processClientRequestSuccess, this.processClientRequestError); + }); + } + + async setTorrentsTrackers({hashes, trackers}: SetTorrentsTrackersOptions): Promise { + return Promise.all( + hashes.map(async (hash) => { + const currentTrackerURLs = await this.getTorrentTrackers(hash).then((currentTrackers) => + currentTrackers.filter((tracker) => tracker.type !== TorrentTrackerType.DHT).map((tracker) => tracker.url), + ); + + await this.clientRequestManager.torrentsRemoveTrackers(hash, currentTrackerURLs); + + return this.clientRequestManager + .torrentsAddTrackers(hash, trackers) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(() => delete this.cachedProperties[hash]); + }), + ).then(() => undefined); + } + + async setTorrentContentsPriority( + hash: string, + {indices, priority}: SetTorrentContentsPropertiesOptions, + ): Promise { + let qbFilePriority = QBittorrentTorrentContentPriority.NORMAL; + + switch (priority) { + case TorrentContentPriority.DO_NOT_DOWNLOAD: + qbFilePriority = QBittorrentTorrentContentPriority.DO_NOT_DOWNLOAD; + break; + case TorrentContentPriority.HIGH: + qbFilePriority = QBittorrentTorrentContentPriority.HIGH; + break; + default: + break; + } + + return this.clientRequestManager + .torrentsFilePrio(hash, indices, qbFilePriority) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async startTorrents({hashes}: StartTorrentsOptions): Promise { + return this.clientRequestManager + .torrentsResume(hashes) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async stopTorrents({hashes}: StopTorrentsOptions): Promise { + return this.clientRequestManager + .torrentsPause(hashes) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async fetchTorrentList(): Promise { + return this.clientRequestManager + .getTorrentInfos() + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(async (infos) => { + this.emit('PROCESS_TORRENT_LIST_START'); + + const torrentList: TorrentList = Object.assign( + {}, + ...(await Promise.all( + infos.map(async (info) => { + if (this.cachedProperties[info.hash] == null) { + const properties = await this.clientRequestManager + .getTorrentProperties(info.hash) + .catch(() => undefined); + const trackers = await this.clientRequestManager.getTorrentTrackers(info.hash).catch(() => undefined); + + if (properties != null && trackers != null && Array.isArray(trackers)) { + this.cachedProperties[info.hash] = { + dateCreated: properties?.creation_date, + isPrivate: trackers[0]?.msg.includes('is private'), + trackerURIs: getDomainsFromURLs( + trackers + .map((tracker) => tracker.url) + .filter((url) => getTorrentTrackerTypeFromURL(url) !== TorrentTrackerType.DHT), + ), + }; + } + } + + const {dateCreated = 0, isPrivate = false, trackerURIs = []} = this.cachedProperties[info.hash] || {}; + + const torrentProperties: TorrentProperties = { + bytesDone: info.completed, + dateAdded: info.added_on, + dateCreated, + directory: info.save_path.slice(0, -1), + downRate: info.dlspeed, + downTotal: info.downloaded, + eta: info.eta >= 8640000 ? -1 : info.eta, + hash: info.hash, + isPrivate, + message: '', // in tracker method + name: info.name, + peersConnected: info.num_leechs, + peersTotal: info.num_incomplete, + percentComplete: info.progress * 100, + priority: 1, + ratio: info.ratio, + seedsConnected: info.num_seeds, + seedsTotal: info.num_complete, + sizeBytes: info.size, + status: getTorrentStatusFromState(info.state), + tags: info.tags === '' ? [] : info.tags.split(','), + trackerURIs, + upRate: info.upspeed, + upTotal: info.uploaded, + }; + + this.emit('PROCESS_TORRENT', torrentProperties); + + return { + [torrentProperties.hash]: torrentProperties, + }; + }), + )), + ); + + const torrentListSummary = { + id: Date.now(), + torrents: torrentList, + }; + + this.emit('PROCESS_TORRENT_LIST_END', torrentListSummary); + return torrentListSummary; + }); + } + + async fetchTransferSummary(): Promise { + return this.clientRequestManager + .getTransferInfo() + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((info) => { + return { + downRate: info.dl_info_speed, + downTotal: info.dl_info_data, + upRate: info.up_info_speed, + upTotal: info.up_info_data, + }; + }); + } + + async getClientSettings(): Promise { + return this.clientRequestManager + .getAppPreferences() + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((preferences) => { + return { + dht: preferences.dht, + dhtPort: preferences.listen_port, + directoryDefault: preferences.save_path.split(',')[0], + networkHttpMaxOpen: preferences.max_connec, + networkLocalAddress: [preferences.announce_ip], + networkMaxOpenFiles: 0, + networkPortOpen: true, + networkPortRandom: preferences.random_port, + networkPortRange: `${preferences.listen_port}`, + piecesHashOnCompletion: false, + piecesMemoryMax: 0, + protocolPex: preferences.pex, + throttleGlobalDownSpeed: preferences.dl_limit, + throttleGlobalUpSpeed: preferences.up_limit, + throttleMaxPeersNormal: 0, + throttleMaxPeersSeed: 0, + throttleMaxDownloads: 0, + throttleMaxDownloadsGlobal: 0, + throttleMaxUploads: preferences.max_uploads_per_torrent, + throttleMaxUploadsGlobal: preferences.max_uploads, + throttleMinPeersNormal: 0, + throttleMinPeersSeed: 0, + trackersNumWant: 0, + }; + }); + } + + async setClientSettings(settings: SetClientSettingsOptions): Promise { + return this.clientRequestManager + .setAppPreferences({ + dht: settings.dht, + save_path: settings.directoryDefault, + max_connec: settings.networkHttpMaxOpen, + announce_ip: settings.networkLocalAddress ? settings.networkLocalAddress[0] : undefined, + random_port: settings.networkPortRandom, + listen_port: settings.networkPortRange ? Number(settings.networkPortRange?.split('-')[0]) : undefined, + pex: settings.protocolPex, + dl_limit: settings.throttleGlobalDownSpeed, + up_limit: settings.throttleGlobalUpSpeed, + max_uploads_per_torrent: settings.throttleMaxUploads, + max_uploads: settings.throttleMaxUploadsGlobal, + }) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } + + async testGateway(): Promise { + return this.clientRequestManager + .updateAuthCookie() + .then(() => this.processClientRequestSuccess(undefined), this.processClientRequestError); + } +} + +export default QBittorrentClientGatewayService; diff --git a/server/services/qBittorrent/clientRequestManager.ts b/server/services/qBittorrent/clientRequestManager.ts new file mode 100644 index 000000000..7d0ff725c --- /dev/null +++ b/server/services/qBittorrent/clientRequestManager.ts @@ -0,0 +1,319 @@ +import axios from 'axios'; +import FormData from 'form-data'; + +import type {QBittorrentConnectionSettings} from '@shared/schema/ClientConnectionSettings'; + +import type {QBittorrentAppPreferences} from './types/QBittorrentAppMethods'; +import type {QBittorrentSyncTorrentPeers} from './types/QBittorrentSyncMethods'; +import type {QBittorrentTransferInfo} from './types/QBittorrentTransferMethods'; +import type { + QBittorrentTorrentContentPriority, + QBittorrentTorrentContents, + QBittorrentTorrentInfos, + QBittorrentTorrentProperties, + QBittorrentTorrentsAddOptions, + QBittorrentTorrentTrackers, +} from './types/QBittorrentTorrentsMethods'; + +class ClientRequestManager { + private connectionSettings: QBittorrentConnectionSettings; + private apiBase: string; + private authCookie?: Promise; + + async authenticate(connectionSettings = this.connectionSettings): Promise { + const {url, username, password} = connectionSettings; + + return axios.get(`${url}/api/v2/auth/login?username=${username}&password=${password}`).then((res) => { + const cookies: Array = res.headers['set-cookie']; + + if (Array.isArray(cookies)) { + return cookies.filter((cookie) => cookie.includes('SID='))[0]; + } + + return undefined; + }); + } + + async updateAuthCookie(connectionSettings?: QBittorrentConnectionSettings): Promise { + let authFailed = false; + + this.authCookie = new Promise((resolve) => { + this.authenticate(connectionSettings).then( + (authCookie) => { + resolve(authCookie); + }, + () => { + authFailed = true; + resolve(undefined); + }, + ); + }); + + await this.authCookie; + + return authFailed ? Promise.reject() : Promise.resolve(); + } + + async getAppPreferences(): Promise { + return axios + .get(`${this.apiBase}/app/preferences`, { + headers: {Cookie: await this.authCookie}, + }) + .then((json) => json.data); + } + + async setAppPreferences(preferences: Partial): Promise { + return axios + .post(`${this.apiBase}/app/setPreferences`, `json=${JSON.stringify(preferences)}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async getTorrentInfos(): Promise { + return axios + .get(`${this.apiBase}/torrents/info`, { + headers: {Cookie: await this.authCookie}, + }) + .then((json) => json.data); + } + + async getTorrentContents(hash: string): Promise { + return axios + .get(`${this.apiBase}/torrents/files?hash=${hash}`, { + headers: {Cookie: await this.authCookie}, + }) + .then((json) => json.data); + } + + async getTorrentProperties(hash: string): Promise { + return axios + .get(`${this.apiBase}/torrents/properties?hash=${hash}`, { + headers: {Cookie: await this.authCookie}, + }) + .then((json) => json.data); + } + + async getTorrentTrackers(hash: string): Promise { + return axios + .get(`${this.apiBase}/torrents/trackers?hash=${hash}`, { + headers: {Cookie: await this.authCookie}, + }) + .then((json) => json.data); + } + + async getTransferInfo(): Promise { + return axios + .get(`${this.apiBase}/transfer/info`, { + headers: {Cookie: await this.authCookie}, + }) + .then((json) => json.data); + } + + async syncTorrentPeers(hash: string): Promise { + return axios + .get(`${this.apiBase}/sync/torrentPeers?hash=${hash}&rid=${Date.now()}`, { + headers: {Cookie: await this.authCookie}, + }) + .then((json) => json.data.peers); + } + + async torrentsPause(hashes: Array): Promise { + return axios + .get(`${this.apiBase}/torrents/pause?hashes=${hashes.join('|')}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsResume(hashes: Array): Promise { + return axios + .get(`${this.apiBase}/torrents/resume?hashes=${hashes.join('|')}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsDelete(hashes: Array, deleteFiles: boolean): Promise { + return axios + .get(`${this.apiBase}/torrents/delete?hashes=${hashes.join('|')}&deleteFiles=${deleteFiles}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsRecheck(hashes: Array): Promise { + return axios + .get(`${this.apiBase}/torrents/recheck?hashes=${hashes.join('|')}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsSetLocation(hashes: Array, location: string): Promise { + return axios + .get(`${this.apiBase}/torrents/setLocation?hashes=${hashes.join('|')}&location=${location}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsSetTopPrio(hashes: Array): Promise { + return axios + .get(`${this.apiBase}/torrents/topPrio?hashes=${hashes.join('|')}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsSetBottomPrio(hashes: Array): Promise { + return axios + .get(`${this.apiBase}/torrents/bottomPrio?hashes=${hashes.join('|')}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsAddFiles(files: Array, options: QBittorrentTorrentsAddOptions): Promise { + const form = new FormData(); + + files.forEach((file, index) => { + form.append('torrents', file, { + filename: `${index}.torrent`, + contentType: 'application/x-bittorrent', + }); + }); + + Object.keys(options).forEach((key) => { + const property = key as keyof typeof options; + form.append(property, `${options[property]}`); + }); + + const headers = form.getHeaders({ + Cookie: await this.authCookie, + 'Content-Length': form.getLengthSync(), + }); + + return axios + .post(`${this.apiBase}/torrents/add`, form, { + headers, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsAddURLs(urls: Array, options: QBittorrentTorrentsAddOptions): Promise { + const form = new FormData(); + + form.append('urls', urls.join('\n')); + + Object.keys(options).forEach((key) => { + const property = key as keyof typeof options; + form.append(property, `${options[property]}`); + }); + + const headers = form.getHeaders({ + Cookie: await this.authCookie, + 'Content-Length': form.getLengthSync(), + }); + + return axios + .post(`${this.apiBase}/torrents/add`, form, { + headers, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsAddTags(hashes: Array, tags: Array): Promise { + return axios + .get(`${this.apiBase}/torrents/addTags?hashes=${hashes.join('|')}&tags=${tags.join(',')}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsRemoveTags(hashes: Array, tags?: Array): Promise { + return axios + .get(`${this.apiBase}/torrents/removeTags`, { + params: { + hashes: hashes.join('|'), + tags: tags?.join(','), + }, + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + async torrentsAddTrackers(hash: string, urls: Array): Promise { + if (urls.length > 0) { + return axios + .get(`${this.apiBase}/torrents/addTrackers`, { + params: { + hash, + urls: urls.join('\n'), + }, + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + } + + async torrentsRemoveTrackers(hash: string, urls: Array): Promise { + if (urls.length > 0) { + return axios + .get(`${this.apiBase}/torrents/removeTrackers`, { + params: { + hash, + urls: urls.join('|'), + }, + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + } + + async torrentsFilePrio(hash: string, ids: Array, priority: QBittorrentTorrentContentPriority) { + return axios + .get(`${this.apiBase}/torrents/filePrio?hash=${hash}&id=${ids.join('|')}&priority=${priority}`, { + headers: {Cookie: await this.authCookie}, + }) + .then(() => { + // returns nothing + }); + } + + constructor(connectionSettings: QBittorrentConnectionSettings) { + this.connectionSettings = connectionSettings; + this.apiBase = `${connectionSettings.url}/api/v2`; + this.updateAuthCookie().catch(() => undefined); + } +} + +export default ClientRequestManager; diff --git a/server/services/qBittorrent/types/QBittorrentAppMethods.ts b/server/services/qBittorrent/types/QBittorrentAppMethods.ts new file mode 100644 index 000000000..d8e273196 --- /dev/null +++ b/server/services/qBittorrent/types/QBittorrentAppMethods.ts @@ -0,0 +1,25 @@ +// WRONG API documentation: dl_limit and up_limit are actually in bytes per second +export interface QBittorrentAppPreferences { + dht: boolean; + pex: boolean; + // Default save path for torrents, separated by slashes + save_path: string; + // Maximum global number of simultaneous connections + max_connec: number; + // Maximum number of simultaneous connections per torrent + max_connec_per_torrent: number; + // Maximum number of upload slots + max_uploads: number; + // Maximum number of upload slots per torrent + max_uploads_per_torrent: number; + // IP announced to trackers + announce_ip: string; + // Port for incoming connections + listen_port: number; + // True if the port is randomly selected + random_port: boolean; + // Global download speed limit in KiB/s; `-1` means no limit is applied + dl_limit: number; + // Global upload speed limit in KiB/s; `-1` means no limit is applied + up_limit: number; +} diff --git a/server/services/qBittorrent/types/QBittorrentSyncMethods.ts b/server/services/qBittorrent/types/QBittorrentSyncMethods.ts new file mode 100644 index 000000000..691adc7ac --- /dev/null +++ b/server/services/qBittorrent/types/QBittorrentSyncMethods.ts @@ -0,0 +1,21 @@ +export interface QBittorrentSyncTorrentPeer { + client: string; + connection: string; + country: string; + country_code: string; + dl_speed: number; + downloaded: number; + up_speed: number; + uploaded: number; + files: string; + flags: string; + flags_desc: string; + ip: string; + port: number; + progress: number; + relevance: number; +} + +export type QBittorrentSyncTorrentPeers = { + [ip_and_port: string]: QBittorrentSyncTorrentPeer; +}; diff --git a/server/services/qBittorrent/types/QBittorrentTorrentsMethods.ts b/server/services/qBittorrent/types/QBittorrentTorrentsMethods.ts new file mode 100644 index 000000000..943686eee --- /dev/null +++ b/server/services/qBittorrent/types/QBittorrentTorrentsMethods.ts @@ -0,0 +1,269 @@ +export type QBittorrentTorrentState = + | 'error' + | 'missingFiles' + | 'uploading' + | 'pausedUP' + | 'queuedUP' + | 'stalledUP' + | 'checkingUP' + | 'forcedUP' + | 'allocating' + | 'downloading' + | 'metaDL' + | 'pausedDL' + | 'queuedDL' + | 'stalledDL' + | 'checkingDL' + | 'forceDL' + | 'checkingResumeData' + | 'moving' + | 'unknown'; + +export interface QBittorrentTorrentInfo { + // Time (Unix Epoch) when the torrent was added to the client + added_on: number; + // Amount of data left to download (bytes) + amount_left: number; + // Whether this torrent is managed by Automatic Torrent Management + auto_tmm: boolean; + // Percentage of file pieces currently available + availability: number; + // Category of the torrent + category: string; + // Amount of transfer data completed (bytes) + completed: number; + // Time (Unix Epoch) when the torrent completed + completion_on: number; + // Torrent download speed limit (bytes/s). -1 if unlimited. + dl_limit: number; + // Torrent download speed (bytes/s) + dlspeed: number; + // Amount of data downloaded + downloaded: number; + // Amount of data downloaded this session + downloaded_session: number; + // Torrent ETA (seconds) + eta: number; + // True if first last piece are prioritized + f_l_piece_prio: boolean; + // True if force start is enabled for this torrent + force_start: boolean; + // Torrent hash + hash: string; + // Last time (Unix Epoch) when a chunk was downloaded/uploaded + last_activity: number; + // Magnet URI corresponding to this torrent + magnet_uri: string; + // Maximum share ratio until torrent is stopped from seeding/uploading + max_ratio: number; + // Maximum seeding time (seconds) until torrent is stopped from seeding + max_seeding_time: number; + // Torrent name + name: string; + // Number of seeds in the swarm + num_complete: number; + // Number of leechers in the swarm + num_incomplete: number; + // Number of leechers connected to + num_leechs: number; + // Number of seeds connected to + num_seeds: number; + // Torrent priority. Returns -1 if queuing is disabled or torrent is in seed mode + priority: number; + // Torrent progress (percentage/100) + progress: number; + // Torrent share ratio. Max ratio value: 9999. + ratio: number; + // TODO (what is different from max_ratio?) + ratio_limit: number; + // Path where this torrent's data is stored + save_path: string; + // TODO (what is different from max_seeding_time?) + seeding_time_limit: number; + // Time (Unix Epoch) when this torrent was last seen complete + seen_complete: number; + // True if sequential download is enabled + seq_dl: boolean; + // Total size (bytes) of files selected for download + size: number; + // Torrent state + state: QBittorrentTorrentState; + // True if super seeding is enabled + super_seeding: boolean; + // Comma-concatenated tag list of the torrent + tags: string; + // Total active time (seconds) + time_active: number; + // Total size (bytes) of all file in this torrent (including unselected ones) + total_size: number; + // The first tracker with working status. Returns empty string if no tracker is working. + tracker: string; + // Torrent upload speed limit (bytes/s). -1 if unlimited. + up_limit: number; + // Amount of data uploaded + uploaded: number; + // Amount of data uploaded this session + uploaded_session: number; + // Torrent upload speed (bytes/s) + upspeed: number; +} + +export type QBittorrentTorrentInfos = Array; + +export interface QBittorrentTorrentsAddOptions { + // Download folder + savepath?: string; + // Cookie sent to download the .torrent file + cookie?: string; + // Category for the torrent + category?: string; + // Tags for the torrent, split by ',' + tags?: string; + // Skip hash checking. Possible values are true, false (default) + skip_checking?: boolean; + // Add torrents in the paused state. Possible values are true, false (default) + paused?: boolean; + // Create the root folder. Possible values are true, false, unset (default) + root_folder?: boolean; + // Rename torrent + rename?: string; + // Set torrent upload speed limit. Unit in bytes/second + upLimit?: number; + // Set torrent download speed limit. Unit in bytes/second + dlLimit?: number; + // Whether Automatic Torrent Management should be used + autoTMM?: boolean; + // Enable sequential download. Possible values are true, false (default) + sequentialDownload?: boolean; + // Prioritize download first last piece. Possible values are true, false (default) + firstLastPiecePrio?: boolean; +} + +export enum QBittorrentTorrentContentPriority { + DO_NOT_DOWNLOAD = 0, + NORMAL = 1, + HIGH = 6, + MAXIMUM = 7, +} + +export interface QBittorrentTorrentContent { + // File name (including relative path) + name: string; + // File size (bytes) + size: number; + // File progress (percentage/100) + progress: number; + // File priority + priority: QBittorrentTorrentContentPriority; + // True if file is seeding/complete + is_seed: boolean; + // The first number is the starting piece index and the second number is the ending piece index (inclusive) + piece_range: Array; + // Percentage of file pieces currently available + availability: number; +} + +export type QBittorrentTorrentContents = Array; + +export interface QBittorrentTorrentProperties { + // Torrent save path + save_path: string; + // Torrent creation date (Unix timestamp) + creation_date: number; + // Torrent piece size (bytes) + piece_size: number; + // Torrent comment + comment: string; + // Total data wasted for torrent (bytes) + total_wasted: number; + // Total data uploaded for torrent (bytes) + total_uploaded: number; + // Total data uploaded this session (bytes) + total_uploaded_session: number; + // Total data downloaded for torrent (bytes) + total_downloaded: number; + // Total data downloaded this session (bytes) + total_downloaded_session: number; + // Torrent upload limit (bytes/s) + up_limit: number; + // Torrent download limit (bytes/s) + dl_limit: number; + // Torrent elapsed time (seconds) + time_elapsed: number; + // Torrent elapsed time while complete (seconds) + seeding_time: number; + // Torrent connection count + nb_connections: number; + // Torrent connection count limit + nb_connections_limit: number; + // Torrent share ratio + share_ratio: number; + // When this torrent was added (unix timestamp) + addition_date: number; + // Torrent completion date (unix timestamp) + completion_date: number; + // Torrent creator + created_by: string; + // Torrent average download speed (bytes/second) + dl_speed_avg: number; + // Torrent download speed (bytes/second) + dl_speed: number; + // Torrent ETA (seconds) + eta: number; + // Last seen complete date (unix timestamp) + last_seen: number; + // Number of peers connected to + peers: number; + // Number of peers in the swarm + peers_total: number; + // Number of pieces owned + pieces_have: number; + // Number of pieces of the torrent + pieces_num: number; + // Number of seconds until the next announce + reannounce: number; + // Number of seeds connected to + seeds: number; + // Number of seeds in the swarm + seeds_total: number; + // Torrent total size (bytes) + total_size: number; + // Torrent average upload speed (bytes/second) + up_speed_avg: number; + // Torrent upload speed (bytes/second) + up_speed: number; +} + +export enum QBittorrentTorrentTrackerStatus { + // Tracker is disabled (used for DHT, PeX, and LSD) + DISABLED = 0, + // Tracker has not been contacted yet + NOT_CONTACTED = 1, + // Tracker has been contacted and is working + CONTACTED = 2, + // Tracker is updating + UPDATING = 3, + // Tracker has been contacted, but it is not working (or doesn't send proper replies) + ERROR = 4, +} + +export interface QBittorrentTorrentTracker { + // Tracker url + url: string; + // Tracker status + status: QBittorrentTorrentTrackerStatus; + // Tracker priority tier. Lower tier trackers are tried before higher tiers + tier: number; + // Number of peers for current torrent, as reported by the tracker + num_peers: number; + // Number of seeds for current torrent, as reported by the tracker + num_seeds: number; + // Number of leeches for current torrent, as reported by the tracker + num_leeches: number; + // Number of completed downloads for current torrent, as reported by the tracker + num_downloaded: number; + // Tracker message (there is no way of knowing what this message is - it's up to tracker admins) + msg: string; +} + +export type QBittorrentTorrentTrackers = Array; diff --git a/server/services/qBittorrent/types/QBittorrentTransferMethods.ts b/server/services/qBittorrent/types/QBittorrentTransferMethods.ts new file mode 100644 index 000000000..cad5a3af2 --- /dev/null +++ b/server/services/qBittorrent/types/QBittorrentTransferMethods.ts @@ -0,0 +1,18 @@ +export interface QBittorrentTransferInfo { + // Global download rate (bytes/s) + dl_info_speed: number; + // Data downloaded this session (bytes) + dl_info_data: number; + // Global upload rate (bytes/s) + up_info_speed: number; + // Data uploaded this session (bytes) + up_info_data: number; + // Download rate limit (bytes/s) + dl_rate_limit: number; + // Upload rate limit (bytes/s) + up_rate_limit: number; + // DHT nodes connected to + dht_nodes: number; + // Connection status + connection_status: 'connected' | 'firewalled' | 'disconnected'; +} diff --git a/server/services/qBittorrent/util/torrentPropertiesUtil.ts b/server/services/qBittorrent/util/torrentPropertiesUtil.ts new file mode 100644 index 000000000..ee57518c6 --- /dev/null +++ b/server/services/qBittorrent/util/torrentPropertiesUtil.ts @@ -0,0 +1,93 @@ +import {TorrentPeer} from '../../../../shared/types/TorrentPeer'; +import {TorrentTrackerType} from '../../../../shared/types/TorrentTracker'; + +import type {QBittorrentTorrentState} from '../types/QBittorrentTorrentsMethods'; +import type {TorrentProperties} from '../../../../shared/types/Torrent'; +import type {TorrentTracker} from '../../../../shared/types/TorrentTracker'; + +export const getTorrentPeerPropertiesFromFlags = (flags: string): Pick => { + const flagsArray = flags.split(' '); + + return { + isEncrypted: flagsArray.includes('E'), + isIncoming: flagsArray.includes('I'), + }; +}; + +export const getTorrentTrackerTypeFromURL = (url: string): TorrentTracker['type'] => { + if (url.startsWith('http')) { + return TorrentTrackerType.HTTP; + } + + if (url.startsWith('udp')) { + return TorrentTrackerType.UDP; + } + + return TorrentTrackerType.DHT; +}; + +export const getTorrentStatusFromState = (state: QBittorrentTorrentState): TorrentProperties['status'] => { + const statuses: TorrentProperties['status'] = []; + + switch (state) { + case 'error': + case 'missingFiles': + statuses.push('error'); + statuses.push('inactive'); + statuses.push('stopped'); + break; + case 'uploading': + statuses.push('complete'); + statuses.push('active'); + statuses.push('seeding'); + break; + case 'pausedUP': + statuses.push('complete'); + statuses.push('inactive'); + statuses.push('stopped'); + break; + case 'queuedUP': + case 'stalledUP': + case 'forcedUP': + statuses.push('complete'); + statuses.push('inactive'); + statuses.push('seeding'); + break; + case 'checkingUP': + statuses.push('complete'); + statuses.push('active'); + statuses.push('checking'); + break; + case 'allocating': + statuses.push('downloading'); + break; + case 'metaDL': + case 'downloading': + statuses.push('active'); + statuses.push('downloading'); + break; + case 'pausedDL': + statuses.push('inactive'); + statuses.push('stopped'); + break; + case 'queuedDL': + case 'stalledDL': + case 'forceDL': + statuses.push('inactive'); + statuses.push('downloading'); + break; + case 'checkingDL': + statuses.push('active'); + statuses.push('checking'); + break; + case 'moving': + case 'checkingResumeData': + case 'unknown': + statuses.push('checking'); + break; + default: + break; + } + + return statuses; +}; diff --git a/server/services/rTorrent/clientGatewayService.ts b/server/services/rTorrent/clientGatewayService.ts new file mode 100644 index 000000000..547a19869 --- /dev/null +++ b/server/services/rTorrent/clientGatewayService.ts @@ -0,0 +1,731 @@ +import fs from 'fs'; +import geoip from 'geoip-country'; +import {moveSync} from 'fs-extra'; +import path from 'path'; +import sanitize from 'sanitize-filename'; + +import type { + AddTorrentByFileOptions, + AddTorrentByURLOptions, + SetTorrentsTagsOptions, +} from '@shared/schema/api/torrents'; +import type { + CheckTorrentsOptions, + DeleteTorrentsOptions, + MoveTorrentsOptions, + SetTorrentContentsPropertiesOptions, + SetTorrentsPriorityOptions, + SetTorrentsTrackersOptions, + StartTorrentsOptions, + StopTorrentsOptions, +} from '@shared/types/api/torrents'; +import type {ClientSettings} from '@shared/types/ClientSettings'; +import type {RTorrentConnectionSettings} from '@shared/schema/ClientConnectionSettings'; +import type {TorrentContent} from '@shared/types/TorrentContent'; +import type {TorrentList, TorrentListSummary, TorrentProperties} from '@shared/types/Torrent'; +import type {TorrentPeer} from '@shared/types/TorrentPeer'; +import type {TorrentTracker} from '@shared/types/TorrentTracker'; +import type {TransferSummary} from '@shared/types/TransferData'; +import type {SetClientSettingsOptions} from '@shared/types/api/client'; + +import ClientGatewayService from '../interfaces/clientGatewayService'; +import ClientRequestManager from './clientRequestManager'; +import {getMethodCalls, processMethodCallResponse} from './util/rTorrentMethodCallUtil'; +import {fetchURLToTempFile, saveBufferToTempFile} from '../../util/tempFileUtil'; +import {setCompleted, setTrackers} from '../../util/torrentFileUtil'; +import { + encodeTags, + getTorrentETAFromProperties, + getTorrentPercentCompleteFromProperties, + getTorrentStatusFromProperties, +} from './util/torrentPropertiesUtil'; +import { + clientSettingMethodCallConfigs, + torrentContentMethodCallConfigs, + torrentListMethodCallConfigs, + torrentPeerMethodCallConfigs, + torrentTrackerMethodCallConfigs, + transferSummaryMethodCallConfigs, +} from './constants/methodCallConfigs'; + +import type {MultiMethodCalls} from './util/rTorrentMethodCallUtil'; + +const filePathMethodCalls = getMethodCalls({ + pathComponents: torrentContentMethodCallConfigs.pathComponents, +}); + +class RTorrentClientGatewayService extends ClientGatewayService { + clientRequestManager = new ClientRequestManager(this.user.client as RTorrentConnectionSettings); + + async addTorrentsByFile({ + files, + destination, + tags, + isBasePath, + isCompleted, + start, + }: Required): Promise { + const torrentPaths = await Promise.all( + files.map(async (file) => { + return saveBufferToTempFile(Buffer.from(file, 'base64'), 'torrent', { + mode: 0o664, + }); + }), + ); + + if (torrentPaths[0] != null) { + return this.addTorrentsByURL({ + urls: torrentPaths as [string, ...string[]], + cookies: {}, + destination, + tags, + isBasePath, + isCompleted, + start, + }); + } + + return Promise.reject(); + } + + async addTorrentsByURL({ + urls, + cookies, + destination, + tags, + isBasePath, + isCompleted, + start, + }: Required): Promise { + await fs.promises.mkdir(destination, {recursive: true}); + + const torrentPaths: Array = ( + await Promise.all( + urls.map(async (url) => { + if (/^(http|https):\/\//.test(url)) { + const domain = url.split('/')[2]; + + // TODO: properly handle error and let frontend know + const torrentPath = await fetchURLToTempFile(url, cookies[domain], 'torrent').catch((e) => + console.error(e), + ); + + if (typeof torrentPath === 'string') { + return torrentPath; + } + + return ''; + } + + // TODO: handle potential other types of downloads + + return url; + }), + ) + ).filter((torrentPath) => torrentPath !== ''); + + if (isCompleted) { + await Promise.all( + torrentPaths.map((torrentPath) => { + if (!fs.existsSync(torrentPath)) { + return false; + } + + return setCompleted(torrentPath, destination, isBasePath); + }), + ); + } + + const methodCalls: MultiMethodCalls = torrentPaths.map((torrentPath) => { + const additionalCalls: Array = []; + + additionalCalls.push(`${isBasePath ? 'd.directory_base.set' : 'd.directory.set'}="${destination}"`); + + if (tags.length > 0) { + additionalCalls.push(`d.custom1.set=${encodeTags(tags)}`); + } + + additionalCalls.push(`d.custom.set=addtime,${Math.round(Date.now() / 1000)}`); + + return { + methodName: start ? 'load.start' : 'load.normal', + params: ['', torrentPath].concat(additionalCalls), + }; + }, []); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(() => { + // returns nothing. + }) || Promise.reject() + ); + } + + async checkTorrents({hashes}: CheckTorrentsOptions): Promise { + const methodCalls = hashes.reduce((accumulator: MultiMethodCalls, hash) => { + accumulator.push({ + methodName: 'd.check_hash', + params: [hash], + }); + + return accumulator; + }, []); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(() => { + // returns nothing. + }) || Promise.reject() + ); + } + + async getTorrentContents(hash: TorrentProperties['hash']): Promise> { + const configs = torrentContentMethodCallConfigs; + return ( + this.clientRequestManager + .methodCall('f.multicall', [hash, ''].concat(getMethodCalls(configs))) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((responses: string[][]) => { + return Promise.all(responses.map((response) => processMethodCallResponse(response, configs))); + }) + .then((processedResponses) => { + return processedResponses.map((content, index) => { + return { + index, + path: content.path, + filename: content.path.split('/').pop() || '', + percentComplete: (content.completedChunks / content.sizeChunks) * 100, + priority: content.priority, + sizeBytes: content.sizeBytes, + }; + }); + }) || Promise.reject() + ); + } + + async getTorrentPeers(hash: TorrentProperties['hash']): Promise> { + const configs = torrentPeerMethodCallConfigs; + return ( + this.clientRequestManager + .methodCall('p.multicall', [hash, ''].concat(getMethodCalls(configs))) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((responses: string[][]) => { + return Promise.all(responses.map((response) => processMethodCallResponse(response, configs))); + }) + .then((processedResponses) => { + return Promise.all( + processedResponses.map(async (processedResponse) => { + return { + ...processedResponse, + country: geoip.lookup(processedResponse.address)?.country || '', + }; + }), + ); + }) || Promise.reject() + ); + } + + async getTorrentTrackers(hash: TorrentProperties['hash']): Promise> { + const configs = torrentTrackerMethodCallConfigs; + return ( + this.clientRequestManager + .methodCall('t.multicall', [hash, ''].concat(getMethodCalls(configs))) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((responses: string[][]) => { + return Promise.all(responses.map((response) => processMethodCallResponse(response, configs))); + }) + .then((processedResponses) => + processedResponses + .filter((processedResponse) => processedResponse.isEnabled) + .map((processedResponse) => { + return { + url: processedResponse.url, + type: processedResponse.type, + }; + }), + ) || Promise.reject() + ); + } + + async moveTorrents({hashes, destination, moveFiles, isBasePath, isCheckHash}: MoveTorrentsOptions): Promise { + try { + await this.stopTorrents({hashes}); + } catch (e) { + return Promise.reject(e); + } + + if (moveFiles) { + const isMultiFile = await this.clientRequestManager + .methodCall('system.multicall', [ + hashes.map((hash) => { + return { + methodName: 'd.is_multi_file', + params: [hash], + }; + }), + ]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((responses: string[][]) => { + return responses.map((response) => { + const [value] = response; + return value === '1'; + }); + }) + .catch(() => undefined); + + if (isMultiFile == null || isMultiFile.length !== hashes.length) { + return Promise.reject(); + } + + hashes.forEach((hash, index) => { + const {directory, name} = this.services?.torrentService.getTorrent(hash) || {}; + + if (directory == null || name == null) { + return; + } + + const sourceDirectory = path.resolve(directory); + const destDirectory = isMultiFile[index] + ? path.resolve(isBasePath ? destination : path.join(destination, name)) + : path.resolve(destination); + + if (sourceDirectory !== destDirectory) { + try { + if (isMultiFile[index]) { + moveSync(sourceDirectory, destDirectory, {overwrite: true}); + } else { + moveSync(path.join(sourceDirectory, name), path.join(destDirectory, name), {overwrite: true}); + } + } catch (err) { + console.error(`Failed to move files to ${destDirectory}.`); + console.error(err); + } + } + }); + } + + const hashesToRestart: Array = []; + const methodCalls = hashes.reduce((accumulator: MultiMethodCalls, hash) => { + accumulator.push({ + methodName: isBasePath ? 'd.directory_base.set' : 'd.directory.set', + params: [hash, destination], + }); + + if (!this.services?.torrentService.getTorrent(hash).status.includes('stopped')) { + hashesToRestart.push(hash); + } + + return accumulator; + }, []); + + try { + await this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError); + } catch (e) { + return Promise.reject(e); + } + + if (isCheckHash) { + try { + await this.checkTorrents({hashes}); + } catch (e) { + return Promise.reject(e); + } + } + + return this.startTorrents({hashes: hashesToRestart}); + } + + async removeTorrents({hashes, deleteData}: DeleteTorrentsOptions): Promise { + const methodCalls = hashes.reduce((accumulator: MultiMethodCalls, hash, index) => { + let eraseFileMethodCallIndex = index; + + // If we're deleting files, we grab each torrents' file list before we remove them. + if (deleteData === true) { + // We offset the indices of these method calls so that we know exactly + // where to retrieve the responses in the future. + const directoryBaseMethodCallIndex = index + hashes.length; + // We also need to ensure that the erase method call occurs after + // our request for information. + eraseFileMethodCallIndex = index + hashes.length * 2; + + accumulator[index] = { + methodName: 'f.multicall', + params: [hash, ''].concat(filePathMethodCalls), + }; + + accumulator[directoryBaseMethodCallIndex] = { + methodName: 'd.directory_base', + params: [hash], + }; + } + + accumulator[eraseFileMethodCallIndex] = { + methodName: 'd.erase', + params: [hash], + }; + + return accumulator; + }, []); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((response) => { + if (deleteData === true) { + const torrentCount = hashes.length; + const filesToDelete = hashes.reduce((accumulator, _hash, hashIndex) => { + const fileList = (response as string[][][][][])[hashIndex][0]; + const directoryBase = (response as string[][])[hashIndex + torrentCount][0]; + + const torrentFilesToDelete = fileList.reduce((fileListAccumulator, file) => { + // We only look at the first path component returned because + // if it's a directory within the torrent, then we'll remove + // the entire directory. + const filePath = path.join(directoryBase, file[0][0]); + + // filePath might be a directory, so it may have already been + // added. If not, we add it. + if (!fileListAccumulator.includes(filePath)) { + fileListAccumulator.push(filePath); + } + + return fileListAccumulator; + }, [] as Array); + + return accumulator.concat(torrentFilesToDelete); + }, [] as Array); + + filesToDelete.forEach((file) => { + try { + if (fs.lstatSync(file).isDirectory()) { + fs.rmdirSync(file, {recursive: true}); + } else { + fs.unlinkSync(file); + } + } catch (error) { + console.error(`Error deleting file: ${file}\n${error}`); + } + }); + } + }) || Promise.reject() + ); + } + + async setTorrentsPriority({hashes, priority}: SetTorrentsPriorityOptions): Promise { + const methodCalls = hashes.reduce((accumulator: MultiMethodCalls, hash) => { + accumulator.push({ + methodName: 'd.priority.set', + params: [hash, `${priority}`], + }); + + accumulator.push({ + methodName: 'd.update_priorities', + params: [hash], + }); + + return accumulator; + }, []); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(() => { + // returns nothing. + }) || Promise.reject() + ); + } + + async setTorrentsTags({hashes, tags}: SetTorrentsTagsOptions): Promise { + const methodCalls = hashes.reduce((accumulator: MultiMethodCalls, hash) => { + accumulator.push({ + methodName: 'd.custom1.set', + params: [hash, encodeTags(tags)], + }); + + return accumulator; + }, []); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(() => { + // returns nothing. + }) || Promise.reject() + ); + } + + async setTorrentsTrackers({hashes, trackers}: SetTorrentsTrackersOptions): Promise { + const methodCalls = hashes.reduce( + (accumulator: MultiMethodCalls, hash) => { + // Disable existing trackers + accumulator.push({ + methodName: 't.multicall', + params: [hash, '', 't.disable='], + }); + + // Insert new trackers + trackers.forEach((tracker) => { + accumulator.push({ + methodName: 'd.tracker.insert', + params: [hash, '0', tracker], + }); + }); + + // Save full session to apply tracker change + accumulator.push({ + methodName: 'd.save_full_session', + params: [hash], + }); + + return accumulator; + }, + [ + { + methodName: 'session.path', + params: [], + }, + ], + ); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(async (response: string[][]) => { + const [session] = response.shift() as string[]; + + if (typeof session === 'string') { + // Deduplicate hashes via Set() to avoid file ops on the same files + await Promise.all( + [...new Set(hashes)].map(async (hash) => { + const torrent = path.join(session, sanitize(`${hash}.torrent`)); + return setTrackers(torrent, trackers); + }), + ); + } + }) || Promise.reject() + ); + } + + async setTorrentContentsPriority( + hash: string, + {indices, priority}: SetTorrentContentsPropertiesOptions, + ): Promise { + const methodCalls = indices.reduce((accumulator: MultiMethodCalls, index) => { + accumulator.push({ + methodName: 'f.priority.set', + params: [`${hash}:f${index}`, `${priority}`], + }); + + return accumulator; + }, []); + + methodCalls.push({ + methodName: 'd.update_priorities', + params: [hash], + }); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(() => { + // returns nothing. + }) || Promise.reject() + ); + } + + async startTorrents({hashes}: StartTorrentsOptions): Promise { + const methodCalls = hashes.reduce((accumulator: MultiMethodCalls, hash) => { + accumulator.push({ + methodName: 'd.open', + params: [hash], + }); + + accumulator.push({ + methodName: 'd.start', + params: [hash], + }); + + return accumulator; + }, []); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(() => { + // returns nothing. + }) || Promise.reject() + ); + } + + async stopTorrents({hashes}: StopTorrentsOptions): Promise { + const methodCalls = hashes.reduce((accumulator: MultiMethodCalls, hash) => { + accumulator.push({ + methodName: 'd.stop', + params: [hash], + }); + + accumulator.push({ + methodName: 'd.close', + params: [hash], + }); + + return accumulator; + }, []); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(() => { + // returns nothing. + }) || Promise.reject() + ); + } + + async fetchTorrentList(): Promise { + const configs = torrentListMethodCallConfigs; + return ( + this.clientRequestManager + .methodCall('d.multicall2', ['', 'main'].concat(getMethodCalls(configs))) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((responses: string[][]) => { + this.emit('PROCESS_TORRENT_LIST_START'); + return Promise.all(responses.map((response) => processMethodCallResponse(response, configs))); + }) + .then(async (processedResponses) => { + const torrentList: TorrentList = Object.assign( + {}, + ...(await Promise.all( + processedResponses.map(async (response) => { + const torrentProperties: TorrentProperties = { + ...response, + status: getTorrentStatusFromProperties(response), + percentComplete: getTorrentPercentCompleteFromProperties(response), + eta: getTorrentETAFromProperties(response), + }; + + this.emit('PROCESS_TORRENT', torrentProperties); + + return { + [response.hash]: torrentProperties, + }; + }), + )), + ); + + const torrentListSummary = { + id: Date.now(), + torrents: torrentList, + }; + + this.emit('PROCESS_TORRENT_LIST_END', torrentListSummary); + return torrentListSummary; + }) || Promise.reject() + ); + } + + async fetchTransferSummary(): Promise { + const configs = transferSummaryMethodCallConfigs; + const methodCalls: MultiMethodCalls = getMethodCalls(configs).map((methodCall) => { + return { + methodName: methodCall, + params: [], + }; + }); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((response) => { + return processMethodCallResponse(response, configs); + }) || Promise.reject() + ); + } + + async getClientSettings(): Promise { + const configs = clientSettingMethodCallConfigs; + const methodCalls: MultiMethodCalls = getMethodCalls(configs).map((methodCall) => { + return { + methodName: methodCall, + params: [], + }; + }); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then((response) => { + return processMethodCallResponse(response, configs); + }) || Promise.reject() + ); + } + + async setClientSettings(settings: SetClientSettingsOptions): Promise { + const configs = clientSettingMethodCallConfigs; + const methodCalls = Object.keys(settings).reduce((accumulator: MultiMethodCalls, key) => { + const property = key as keyof SetClientSettingsOptions; + let methodName = ''; + let param = settings[property]; + + if (param == null) { + return accumulator; + } + + switch (property) { + case 'dht': + methodName = 'dht.mode.set'; + param = (param as ClientSettings[typeof property]) ? 'auto' : 'disable'; + break; + case 'piecesMemoryMax': + methodName = `${configs[property].methodCall}.set`; + param = (param as ClientSettings[typeof property]) * 1024 * 1024; + break; + default: + methodName = `${configs[property].methodCall}.set`; + break; + } + + if (typeof param === 'boolean') { + param = param ? '1' : '0'; + } + + accumulator.push({ + methodName, + params: ['', `${param}`], + }); + + return accumulator; + }, []); + + return ( + this.clientRequestManager + .methodCall('system.multicall', [methodCalls]) + .then(this.processClientRequestSuccess, this.processClientRequestError) + .then(() => { + // returns nothing. + }) || Promise.reject() + ); + } + + async testGateway(): Promise { + return this.clientRequestManager + .methodCall('system.methodExist', ['system.multicall']) + .then(() => this.processClientRequestSuccess(undefined), this.processClientRequestError); + } +} + +export default RTorrentClientGatewayService; diff --git a/server/services/rTorrent/clientRequestManager.ts b/server/services/rTorrent/clientRequestManager.ts new file mode 100644 index 000000000..b9764d794 --- /dev/null +++ b/server/services/rTorrent/clientRequestManager.ts @@ -0,0 +1,94 @@ +import type {RTorrentConnectionSettings} from '@shared/schema/ClientConnectionSettings'; + +import scgiUtil from './util/scgiUtil'; + +import type {MultiMethodCalls} from './util/rTorrentMethodCallUtil'; + +type MethodCallParameters = Array; + +class ClientRequestManager { + connectionSettings: RTorrentConnectionSettings; + isRequestPending = false; + lastResponseTimestamp = 0; + pendingRequests: Array<{ + methodName: string; + parameters: MethodCallParameters; + resolve: (value?: Record) => void; + reject: (error?: NodeJS.ErrnoException) => void; + }> = []; + + constructor(connectionSettings: RTorrentConnectionSettings) { + this.connectionSettings = connectionSettings; + } + + handleRequestEnd() { + this.isRequestPending = false; + + // We avoid initiating any deferred requests until at least 250ms have + // since the previous response. + const currentTimestamp = Date.now(); + const timeSinceLastResponse = currentTimestamp - this.lastResponseTimestamp; + + if (timeSinceLastResponse <= 250) { + const delay = 250 - timeSinceLastResponse; + setTimeout(this.sendDeferredMethodCall, delay); + this.lastResponseTimestamp = currentTimestamp + delay; + } else { + this.sendDeferredMethodCall(); + this.lastResponseTimestamp = currentTimestamp; + } + } + + sendDeferredMethodCall = () => { + const nextRequest = this.pendingRequests.shift(); + if (nextRequest == null) { + return; + } + + this.isRequestPending = true; + this.sendMethodCall(nextRequest.methodName, nextRequest.parameters).then(nextRequest.resolve, nextRequest.reject); + }; + + sendMethodCall = (methodName: string, parameters: MethodCallParameters) => { + const connectionMethod = + this.connectionSettings.type === 'socket' + ? { + socketPath: this.connectionSettings.socket, + } + : { + host: this.connectionSettings.host, + port: this.connectionSettings.port, + }; + + return scgiUtil.methodCall(connectionMethod, methodName, parameters).then( + (response) => { + this.handleRequestEnd(); + return response; + }, + (error) => { + this.handleRequestEnd(); + throw error; + }, + ); + }; + + methodCall = (methodName: string, parameters: MethodCallParameters) => { + // We only allow one request at a time. + if (this.isRequestPending) { + return new Promise( + (resolve: (value?: Record) => void, reject: (error?: NodeJS.ErrnoException) => void) => { + this.pendingRequests.push({ + methodName, + parameters, + resolve, + reject, + }); + }, + ); + } + this.isRequestPending = true; + return this.sendMethodCall(methodName, parameters); + }; +} + +export default ClientRequestManager; diff --git a/server/services/rTorrent/constants/methodCallConfigs/clientSetting.ts b/server/services/rTorrent/constants/methodCallConfigs/clientSetting.ts new file mode 100644 index 000000000..b6f830f1a --- /dev/null +++ b/server/services/rTorrent/constants/methodCallConfigs/clientSetting.ts @@ -0,0 +1,121 @@ +import {numberTransformer, stringArrayTransformer} from '../../util/rTorrentMethodCallUtil'; + +const clientSettingMethodCallConfigs = { + dht: { + methodCall: 'dht.statistics', + transformValue: (value: unknown): boolean => { + const [stats] = value as Array>; + return stats.dht !== 'disable'; + }, + }, + dhtPort: { + methodCall: 'dht.port', + transformValue: numberTransformer, + }, + directoryDefault: { + methodCall: 'directory.default', + transformValue: (value: unknown) => { + const [directory] = value as Array; + return directory; + }, + }, + networkHttpMaxOpen: { + methodCall: 'network.http.max_open', + transformValue: numberTransformer, + }, + networkLocalAddress: { + methodCall: 'network.local_address', + transformValue: stringArrayTransformer, + }, + networkMaxOpenFiles: { + methodCall: 'network.max_open_files', + transformValue: numberTransformer, + }, + networkPortOpen: { + methodCall: 'network.port_open', + transformValue: (value: unknown) => { + const [portOpen] = value as Array; + return portOpen === '1'; + }, + }, + networkPortRandom: { + methodCall: 'network.port_random', + transformValue: (value: unknown) => { + const [portRandom] = value as Array; + return portRandom === '1'; + }, + }, + networkPortRange: { + methodCall: 'network.port_range', + transformValue: (value: unknown) => { + const [portRange] = value as Array; + return portRange; + }, + }, + piecesHashOnCompletion: { + methodCall: 'pieces.hash.on_completion', + transformValue: (value: unknown) => { + const [hashOnCompletion] = value as Array; + return hashOnCompletion === '1'; + }, + }, + piecesMemoryMax: { + methodCall: 'pieces.memory.max', + transformValue: (value: unknown) => { + return Number(value) / (1024 * 1024); + }, + }, + protocolPex: { + methodCall: 'protocol.pex', + transformValue: (value: unknown) => { + const [protocolPex] = value as Array; + return protocolPex === '1'; + }, + }, + throttleGlobalDownSpeed: { + methodCall: 'throttle.global_down.max_rate', + transformValue: numberTransformer, + }, + throttleGlobalUpSpeed: { + methodCall: 'throttle.global_up.max_rate', + transformValue: numberTransformer, + }, + throttleMaxPeersNormal: { + methodCall: 'throttle.max_peers.normal', + transformValue: numberTransformer, + }, + throttleMaxPeersSeed: { + methodCall: 'throttle.max_peers.seed', + transformValue: numberTransformer, + }, + throttleMaxDownloads: { + methodCall: 'throttle.max_downloads', + transformValue: numberTransformer, + }, + throttleMaxDownloadsGlobal: { + methodCall: 'throttle.max_downloads.global', + transformValue: numberTransformer, + }, + throttleMaxUploads: { + methodCall: 'throttle.max_uploads', + transformValue: numberTransformer, + }, + throttleMaxUploadsGlobal: { + methodCall: 'throttle.max_uploads.global', + transformValue: numberTransformer, + }, + throttleMinPeersNormal: { + methodCall: 'throttle.min_peers.normal', + transformValue: numberTransformer, + }, + throttleMinPeersSeed: { + methodCall: 'throttle.min_peers.seed', + transformValue: numberTransformer, + }, + trackersNumWant: { + methodCall: 'trackers.numwant', + transformValue: numberTransformer, + }, +} as const; + +export default clientSettingMethodCallConfigs; diff --git a/server/services/rTorrent/constants/methodCallConfigs/index.ts b/server/services/rTorrent/constants/methodCallConfigs/index.ts new file mode 100644 index 000000000..621e82280 --- /dev/null +++ b/server/services/rTorrent/constants/methodCallConfigs/index.ts @@ -0,0 +1,6 @@ +export {default as clientSettingMethodCallConfigs} from './clientSetting'; +export {default as torrentContentMethodCallConfigs} from './torrentContent'; +export {default as torrentListMethodCallConfigs} from './torrentList'; +export {default as torrentPeerMethodCallConfigs} from './torrentPeer'; +export {default as torrentTrackerMethodCallConfigs} from './torrentTracker'; +export {default as transferSummaryMethodCallConfigs} from './transferSummary'; diff --git a/server/services/rTorrent/constants/methodCallConfigs/torrentContent.ts b/server/services/rTorrent/constants/methodCallConfigs/torrentContent.ts new file mode 100644 index 000000000..83caeac3d --- /dev/null +++ b/server/services/rTorrent/constants/methodCallConfigs/torrentContent.ts @@ -0,0 +1,30 @@ +import {stringTransformer, stringArrayTransformer, numberTransformer} from '../../util/rTorrentMethodCallUtil'; + +const torrentContentMethodCallConfigs = { + path: { + methodCall: 'f.path=', + transformValue: stringTransformer, + }, + pathComponents: { + methodCall: 'f.path_components=', + transformValue: stringArrayTransformer, + }, + priority: { + methodCall: 'f.priority=', + transformValue: numberTransformer, + }, + sizeBytes: { + methodCall: 'f.size_bytes=', + transformValue: numberTransformer, + }, + sizeChunks: { + methodCall: 'f.size_chunks=', + transformValue: numberTransformer, + }, + completedChunks: { + methodCall: 'f.completed_chunks=', + transformValue: numberTransformer, + }, +} as const; + +export default torrentContentMethodCallConfigs; diff --git a/server/services/rTorrent/constants/methodCallConfigs/torrentList.ts b/server/services/rTorrent/constants/methodCallConfigs/torrentList.ts new file mode 100644 index 000000000..13693f451 --- /dev/null +++ b/server/services/rTorrent/constants/methodCallConfigs/torrentList.ts @@ -0,0 +1,150 @@ +import {getDomainsFromURLs} from '../../../../util/torrentPropertiesUtil'; +import {stringTransformer, booleanTransformer, numberTransformer} from '../../util/rTorrentMethodCallUtil'; + +const torrentListMethodCallConfigs = { + hash: { + methodCall: 'd.hash=', + transformValue: stringTransformer, + }, + name: { + methodCall: 'd.name=', + transformValue: stringTransformer, + }, + message: { + methodCall: 'd.message=', + transformValue: stringTransformer, + }, + state: { + methodCall: 'd.state=', + transformValue: stringTransformer, + }, + isActive: { + methodCall: 'd.is_active=', + transformValue: booleanTransformer, + }, + isComplete: { + methodCall: 'd.complete=', + transformValue: booleanTransformer, + }, + isPrivate: { + methodCall: 'd.is_private=', + transformValue: booleanTransformer, + }, + isOpen: { + methodCall: 'd.is_open=', + transformValue: booleanTransformer, + }, + isHashing: { + methodCall: 'd.hashing=', + transformValue: (value: unknown): boolean => { + return value !== '0'; + }, + }, + priority: { + methodCall: 'd.priority=', + transformValue: numberTransformer, + }, + upRate: { + methodCall: 'd.up.rate=', + transformValue: numberTransformer, + }, + upTotal: { + methodCall: 'd.up.total=', + transformValue: numberTransformer, + }, + downRate: { + methodCall: 'd.down.rate=', + transformValue: numberTransformer, + }, + downTotal: { + methodCall: 'd.down.total=', + transformValue: numberTransformer, + }, + ratio: { + methodCall: 'd.ratio=', + transformValue: (value: unknown): number => { + return (value as number) / 1000; + }, + }, + bytesDone: { + methodCall: 'd.bytes_done=', + transformValue: numberTransformer, + }, + sizeBytes: { + methodCall: 'd.size_bytes=', + transformValue: numberTransformer, + }, + directory: { + methodCall: 'd.directory=', + transformValue: stringTransformer, + }, + dateAdded: { + methodCall: 'd.custom=addtime', + transformValue: numberTransformer, + }, + dateCreated: { + methodCall: 'd.creation_date=', + transformValue: numberTransformer, + }, + tags: { + methodCall: 'd.custom1=', + transformValue: (value: unknown): string[] => { + if (value === '' || typeof value !== 'string') { + return []; + } + + return value + .split(',') + .sort() + .map((tag) => decodeURIComponent(tag)); + }, + }, + trackerURIs: { + methodCall: 'cat="$t.multicall=d.hash=,t.is_enabled=,t.url=,cat={|||}"', + transformValue: (value: unknown): string[] => { + if (typeof value !== 'string') { + return []; + } + return getDomainsFromURLs( + value.split('|||').reduce((trackers: Array, tracker) => { + // Only count enabled trackers + if (tracker.charAt(0) === '0') { + return trackers; + } + + trackers.push(tracker.substr(1)); + + return trackers; + }, []), + ); + }, + }, + seedsConnected: { + methodCall: 'd.peers_complete=', + transformValue: numberTransformer, + }, + seedsTotal: { + methodCall: 'cat="$t.multicall=d.hash=,t.scrape_complete=,cat={|||}"', + transformValue: (value: unknown): number => { + if (typeof value !== 'string') { + return 0; + } + return Number(value.substr(0, value.indexOf('|||'))); + }, + }, + peersConnected: { + methodCall: 'd.peers_accounted=', + transformValue: numberTransformer, + }, + peersTotal: { + methodCall: 'cat="$t.multicall=d.hash=,t.scrape_incomplete=,cat={|||}"', + transformValue: (value: unknown): number => { + if (typeof value !== 'string') { + return 0; + } + return Number(value.substr(0, value.indexOf('|||'))); + }, + }, +} as const; + +export default torrentListMethodCallConfigs; diff --git a/server/services/rTorrent/constants/methodCallConfigs/torrentPeer.ts b/server/services/rTorrent/constants/methodCallConfigs/torrentPeer.ts new file mode 100644 index 000000000..3addee1b6 --- /dev/null +++ b/server/services/rTorrent/constants/methodCallConfigs/torrentPeer.ts @@ -0,0 +1,34 @@ +import {stringTransformer, booleanTransformer, numberTransformer} from '../../util/rTorrentMethodCallUtil'; + +const torrentPeerMethodCallConfigs = { + address: { + methodCall: 'p.address=', + transformValue: stringTransformer, + }, + clientVersion: { + methodCall: 'p.client_version=', + transformValue: stringTransformer, + }, + completedPercent: { + methodCall: 'p.completed_percent=', + transformValue: numberTransformer, + }, + downloadRate: { + methodCall: 'p.down_rate=', + transformValue: numberTransformer, + }, + uploadRate: { + methodCall: 'p.up_rate=', + transformValue: numberTransformer, + }, + isEncrypted: { + methodCall: 'p.is_encrypted=', + transformValue: booleanTransformer, + }, + isIncoming: { + methodCall: 'p.is_incoming=', + transformValue: booleanTransformer, + }, +} as const; + +export default torrentPeerMethodCallConfigs; diff --git a/server/services/rTorrent/constants/methodCallConfigs/torrentTracker.ts b/server/services/rTorrent/constants/methodCallConfigs/torrentTracker.ts new file mode 100644 index 000000000..95695dead --- /dev/null +++ b/server/services/rTorrent/constants/methodCallConfigs/torrentTracker.ts @@ -0,0 +1,18 @@ +import {booleanTransformer, numberTransformer, stringTransformer} from '../../util/rTorrentMethodCallUtil'; + +const torrentTrackerMethodCallConfigs = { + url: { + methodCall: 't.url=', + transformValue: stringTransformer, + }, + type: { + methodCall: 't.type=', + transformValue: numberTransformer, + }, + isEnabled: { + methodCall: 't.is_enabled=', + transformValue: booleanTransformer, + }, +} as const; + +export default torrentTrackerMethodCallConfigs; diff --git a/server/services/rTorrent/constants/methodCallConfigs/transferSummary.ts b/server/services/rTorrent/constants/methodCallConfigs/transferSummary.ts new file mode 100644 index 000000000..03ad94563 --- /dev/null +++ b/server/services/rTorrent/constants/methodCallConfigs/transferSummary.ts @@ -0,0 +1,22 @@ +import {numberTransformer} from '../../util/rTorrentMethodCallUtil'; + +const transferSummaryMethodCallConfigs = { + upRate: { + methodCall: 'throttle.global_up.rate', + transformValue: numberTransformer, + }, + upTotal: { + methodCall: 'throttle.global_up.total', + transformValue: numberTransformer, + }, + downRate: { + methodCall: 'throttle.global_down.rate', + transformValue: numberTransformer, + }, + downTotal: { + methodCall: 'throttle.global_down.total', + transformValue: numberTransformer, + }, +} as const; + +export default transferSummaryMethodCallConfigs; diff --git a/server/services/rTorrent/util/numberUtils.ts b/server/services/rTorrent/util/numberUtils.ts new file mode 100644 index 000000000..7ac2c92eb --- /dev/null +++ b/server/services/rTorrent/util/numberUtils.ts @@ -0,0 +1,6 @@ +const truncateTo = (num: number, precision = 0) => { + const factor = 10 ** precision; + return Math.floor(num * factor) / factor; +}; + +export default truncateTo; diff --git a/server/util/rTorrentDeserializer.js b/server/services/rTorrent/util/rTorrentDeserializer.js similarity index 77% rename from server/util/rTorrentDeserializer.js rename to server/services/rTorrent/util/rTorrentDeserializer.js index 3726caea0..008f59817 100644 --- a/server/util/rTorrentDeserializer.js +++ b/server/services/rTorrent/util/rTorrentDeserializer.js @@ -1,4 +1,4 @@ -const saxen = require('saxen'); +import {Parser} from 'saxen'; let stackMarks; let dataStack; @@ -7,7 +7,18 @@ let dataIsVal; let endOfResponse; let rejectCallback; -const openTag = elementName => { +let parserInit = false; +const parser = new Parser(); + +const unescapeXMLString = (value) => + value + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/'/g, "'") + .replace(/"/g, '"') + .replace(/&/g, '&'); + +const openTag = (elementName) => { if (elementName === 'array' || elementName === 'struct') { stackMarks.push(dataStack.length); } @@ -15,15 +26,15 @@ const openTag = elementName => { dataIsVal = elementName === 'value'; }; -const onText = value => { - tmpData.push(value); +const onText = (value) => { + tmpData.push(unescapeXMLString(value)); }; -const onError = err => { +const onError = (err) => { rejectCallback(err); }; -const closeTag = elementName => { +const closeTag = (elementName) => { let stackMark; const tagValue = tmpData.join(''); // types that rTorrent uses: @@ -79,6 +90,19 @@ const closeTag = elementName => { } }; +const initParser = () => { + if (parserInit === true) { + return; + } + + parser.on('openTag', openTag); + parser.on('closeTag', closeTag); + parser.on('text', onText); + parser.on('error', onError); + + parserInit = true; +}; + const deserialize = (data, resolve, reject) => { stackMarks = []; dataStack = []; @@ -86,16 +110,15 @@ const deserialize = (data, resolve, reject) => { dataIsVal = false; endOfResponse = false; rejectCallback = reject; - const parser = new saxen.Parser(); - parser.on('openTag', openTag); - parser.on('closeTag', closeTag); - parser.on('text', onText); - parser.on('error', onError); + + initParser(); parser.parse(data); + if (endOfResponse) { return resolve(dataStack[0]); } + return reject('truncated response was received'); }; -module.exports = {deserialize}; +export default {deserialize}; diff --git a/server/services/rTorrent/util/rTorrentMethodCallUtil.ts b/server/services/rTorrent/util/rTorrentMethodCallUtil.ts new file mode 100644 index 000000000..02ed6b5a4 --- /dev/null +++ b/server/services/rTorrent/util/rTorrentMethodCallUtil.ts @@ -0,0 +1,53 @@ +export interface MethodCallConfig { + readonly methodCall: string; + readonly transformValue: (value: unknown) => string | boolean | number | string[] | Record; +} + +export type MethodCallConfigs = Readonly<{ + [propLabel: string]: MethodCallConfig; +}>; + +export type MultiMethodCalls = Array<{ + methodName: string; + params: Array; +}>; + +export const stringTransformer = (value: unknown): string => { + return value as string; +}; + +export const stringArrayTransformer = (value: unknown): string[] => { + return value as string[]; +}; + +export const booleanTransformer = (value: unknown): boolean => { + return value === '1'; +}; + +export const numberTransformer = (value: unknown): number => { + return Number(value); +}; + +export const getMethodCalls = (configs: MethodCallConfigs) => { + return Object.values(configs).map((config) => config.methodCall); +}; + +export const processMethodCallResponse = async ( + response: Array[0]>, + configs: T, +): Promise< + { + [propLabel in P]: ReturnType; + } +> => { + return Object.assign( + {}, + ...(await Promise.all( + Object.keys(configs).map(async (propLabel, index) => { + return { + [propLabel]: configs[propLabel].transformValue(response[index]), + }; + }), + )), + ); +}; diff --git a/server/util/scgiUtil.js b/server/services/rTorrent/util/scgiUtil.js similarity index 80% rename from server/util/scgiUtil.js rename to server/services/rTorrent/util/scgiUtil.js index 99e6c701b..4043cd431 100644 --- a/server/util/scgiUtil.js +++ b/server/services/rTorrent/util/scgiUtil.js @@ -1,13 +1,13 @@ -const net = require('net'); -const Serializer = require('xmlrpc/lib/serializer'); -const rTorrentDeserializer = require('./rTorrentDeserializer'); +import net from 'net'; +import Serializer from 'xmlrpc/lib/serializer'; +import rTorrentDeserializer from './rTorrentDeserializer'; const NULL_CHAR = String.fromCharCode(0); -const bufferStream = stream => { +const bufferStream = (stream) => { const chunks = []; return new Promise((resolve, reject) => { - stream.on('data', chunk => chunks.push(Buffer.from(chunk))); + stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); stream.on('error', reject); stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); }); @@ -34,10 +34,10 @@ const methodCall = (connectionMethod, methodName, parameters) => stream.end(`${headerLength}:${headerItems.join('')},${xml}`); bufferStream(stream) - .then(data => { + .then((data) => { rTorrentDeserializer.deserialize(data, resolve, reject); }) .catch(reject); }); -module.exports = {methodCall}; +export default {methodCall}; diff --git a/server/services/rTorrent/util/torrentPropertiesUtil.ts b/server/services/rTorrent/util/torrentPropertiesUtil.ts new file mode 100644 index 000000000..8bdbeaecc --- /dev/null +++ b/server/services/rTorrent/util/torrentPropertiesUtil.ts @@ -0,0 +1,93 @@ +import truncateTo from './numberUtils'; + +import type {TorrentProperties} from '../../../../shared/types/Torrent'; +import type {TorrentStatus} from '../../../../shared/constants/torrentStatusMap'; + +export const getTorrentETAFromProperties = ( + processingTorrentProperties: Record, +): TorrentProperties['eta'] => { + const {downRate, bytesDone, sizeBytes} = processingTorrentProperties; + + if (typeof downRate !== 'number' || typeof bytesDone !== 'number' || typeof sizeBytes !== 'number') { + return -1; + } + + if (downRate > 0) { + return (sizeBytes - bytesDone) / downRate; + } + + return -1; +}; + +export const getTorrentPercentCompleteFromProperties = ( + processingTorrentProperties: Record, +): TorrentProperties['percentComplete'] => { + const {bytesDone, sizeBytes} = processingTorrentProperties; + + if (typeof bytesDone !== 'number' || typeof sizeBytes !== 'number') { + return 0; + } + + const percentComplete = (bytesDone / sizeBytes) * 100; + + if (percentComplete > 0 && percentComplete < 10) { + return Number(truncateTo(percentComplete, 2)); + } + if (percentComplete > 10 && percentComplete < 100) { + return Number(truncateTo(percentComplete, 1)); + } + + return percentComplete; +}; + +export const getTorrentStatusFromProperties = ( + processingTorrentProperties: Record, +): TorrentProperties['status'] => { + const {isHashing, isComplete, isOpen, upRate, downRate, state, message} = processingTorrentProperties; + + const torrentStatus: Array = []; + + if (isHashing) { + torrentStatus.push('checking'); + } else if (isComplete && isOpen && state === '1') { + torrentStatus.push('complete'); + torrentStatus.push('seeding'); + } else if (isComplete && isOpen && state === '0') { + torrentStatus.push('stopped'); + } else if (isComplete && !isOpen) { + torrentStatus.push('stopped'); + torrentStatus.push('complete'); + } else if (!isComplete && isOpen && state === '1') { + torrentStatus.push('downloading'); + } else if (!isComplete && isOpen && state === '0') { + torrentStatus.push('stopped'); + } else if (!isComplete && !isOpen) { + torrentStatus.push('stopped'); + } + + if (typeof message === 'string' && message.length) { + torrentStatus.push('error'); + } + + if (upRate !== 0 || downRate !== 0) { + torrentStatus.push('active'); + } else { + torrentStatus.push('inactive'); + } + + return torrentStatus; +}; + +export const encodeTags = (tags: TorrentProperties['tags']): string => { + return tags + .reduce((accumulator: Array, currentTag) => { + const tag = encodeURIComponent(currentTag.trim()); + + if (tag !== '' && accumulator.indexOf(tag) === -1) { + accumulator.push(tag); + } + + return accumulator; + }, []) + .join(','); +}; diff --git a/server/services/settingService.ts b/server/services/settingService.ts new file mode 100644 index 000000000..680220438 --- /dev/null +++ b/server/services/settingService.ts @@ -0,0 +1,107 @@ +import Datastore from 'nedb'; +import path from 'path'; + +import type {FloodSettings} from '@shared/types/FloodSettings'; + +import config from '../../config'; +import BaseService from './BaseService'; + +interface SettingRecord { + id: string; + data: unknown; +} + +interface SettingServiceEvents { + SETTINGS_CHANGE: (changeSettings: Partial) => void; +} + +class SettingService extends BaseService { + db = this.loadDatabase(); + + loadDatabase(): Datastore { + const userId = this.user._id; + + const database = new Datastore({ + autoload: true, + filename: path.join(config.dbPath, userId, 'settings', 'settings.db'), + }); + + return database; + } + + async get(property: keyof FloodSettings | null): Promise> { + return new Promise((resolve, reject) => { + this.db + .find( + property + ? { + id: property, + } + : {}, + ) + .exec(async (err, docs) => { + if (err) { + reject(err); + } + + resolve( + Object.assign( + {}, + ...(await Promise.all( + docs.map(async (doc) => { + return { + [doc.id]: doc.data, + }; + }), + )), + ), + ); + }); + }); + } + + async set(changedSettings: Partial): Promise> { + const savedSettings: typeof changedSettings = {}; + + if (changedSettings) { + let error: Error | null = null; + + await Promise.all( + Object.keys(changedSettings).map((key) => { + return new Promise((resolve, reject) => { + const property = key as keyof FloodSettings; + return this.db.update( + {id: property}, + {$set: {data: changedSettings[property]}}, + {upsert: true}, + (err: Error | null) => { + if (err) { + reject(err); + return; + } + resolve(property); + }, + ); + }) + .then((property) => { + Object.assign(savedSettings, { + [property]: changedSettings[property], + }); + }) + .catch((e) => { + error = e; + }); + }), + ); + + if (error) { + return Promise.reject(error); + } + } + + this.emit('SETTINGS_CHANGE', savedSettings); + return savedSettings; + } +} + +export default SettingService; diff --git a/server/services/taxonomyService.js b/server/services/taxonomyService.js deleted file mode 100644 index 9d14f4fba..000000000 --- a/server/services/taxonomyService.js +++ /dev/null @@ -1,133 +0,0 @@ -const BaseService = require('./BaseService'); -const clientGatewayServiceEvents = require('../constants/clientGatewayServiceEvents'); -const objectUtil = require('../../shared/util/objectUtil'); -const taxonomyServiceEvents = require('../constants/taxonomyServiceEvents'); -const torrentStatusMap = require('../../shared/constants/torrentStatusMap'); - -class TaxonomyService extends BaseService { - constructor(...serviceConfig) { - super(...serviceConfig); - - this.lastStatusCounts = {all: 0}; - this.lastTagCounts = {all: 0}; - this.lastTrackerCounts = {all: 0}; - - this.statusCounts = {all: 0}; - this.tagCounts = {all: 0}; - this.trackerCounts = {all: 0}; - - this.handleProcessTorrent = this.handleProcessTorrent.bind(this); - this.handleProcessTorrentListStart = this.handleProcessTorrentListStart.bind(this); - this.handleProcessTorrentListEnd = this.handleProcessTorrentListEnd.bind(this); - - const {clientGatewayService} = this.services; - - clientGatewayService.on(clientGatewayServiceEvents.PROCESS_TORRENT_LIST_START, this.handleProcessTorrentListStart); - - clientGatewayService.on(clientGatewayServiceEvents.PROCESS_TORRENT_LIST_END, this.handleProcessTorrentListEnd); - - clientGatewayService.on(clientGatewayServiceEvents.PROCESS_TORRENT, this.handleProcessTorrent); - } - - destroy() { - const {clientGatewayService} = this.services; - - clientGatewayService.removeListener( - clientGatewayServiceEvents.PROCESS_TORRENT_LIST_START, - this.handleProcessTorrentListStart, - ); - - clientGatewayService.removeListener( - clientGatewayServiceEvents.PROCESS_TORRENT_LIST_END, - this.handleProcessTorrentListEnd, - ); - - clientGatewayService.removeListener(clientGatewayServiceEvents.PROCESS_TORRENT, this.handleProcessTorrent); - } - - getTaxonomy() { - return { - id: Date.now(), - taxonomy: { - statusCounts: this.statusCounts, - tagCounts: this.tagCounts, - trackerCounts: this.trackerCounts, - }, - }; - } - - handleProcessTorrentListStart() { - this.lastStatusCounts = Object.assign({}, this.statusCounts); - this.lastTagCounts = Object.assign({}, this.tagCounts); - this.lastTrackerCounts = Object.assign({}, this.trackerCounts); - - torrentStatusMap.statusShorthand.forEach(statusShorthand => { - this.statusCounts[torrentStatusMap[statusShorthand]] = 0; - }); - - this.statusCounts.all = 0; - this.tagCounts = {all: 0}; - this.trackerCounts = {all: 0}; - } - - handleProcessTorrentListEnd(torrentList) { - const {length = 0} = torrentList; - - this.statusCounts.all = length; - this.tagCounts.all = length; - this.trackerCounts.all = length; - - const taxonomyDiffs = { - statusCounts: objectUtil.getDiff(this.lastStatusCounts, this.statusCounts), - tagCounts: objectUtil.getDiff(this.lastTagCounts, this.tagCounts), - trackerCounts: objectUtil.getDiff(this.lastTrackerCounts, this.trackerCounts), - }; - - const didDiffChange = Object.keys(taxonomyDiffs).some(diffKey => taxonomyDiffs[diffKey].length > 0); - - if (didDiffChange) { - this.emit(taxonomyServiceEvents.TAXONOMY_DIFF_CHANGE, { - diff: taxonomyDiffs, - id: Date.now(), - }); - } - } - - handleProcessTorrent(torrentDetails) { - this.incrementStatusCounts(torrentDetails.status); - this.incrementTagCounts(torrentDetails.tags); - this.incrementTrackerCounts(torrentDetails.trackerURIs); - } - - incrementStatusCounts(statuses) { - statuses.forEach(status => { - this.statusCounts[torrentStatusMap[status]]++; - }); - } - - incrementTagCounts(tags) { - if (tags.length === 0) { - tags = ['untagged']; - } - - tags.forEach(tag => { - if (this.tagCounts[tag] != null) { - this.tagCounts[tag]++; - } else { - this.tagCounts[tag] = 1; - } - }); - } - - incrementTrackerCounts(trackers) { - trackers.forEach(tracker => { - if (this.trackerCounts[tracker] != null) { - this.trackerCounts[tracker]++; - } else { - this.trackerCounts[tracker] = 1; - } - }); - } -} - -module.exports = TaxonomyService; diff --git a/server/services/taxonomyService.ts b/server/services/taxonomyService.ts new file mode 100644 index 000000000..03ac45dfb --- /dev/null +++ b/server/services/taxonomyService.ts @@ -0,0 +1,130 @@ +import jsonpatch, {Operation} from 'fast-json-patch'; + +import BaseService from './BaseService'; +import torrentStatusMap from '../../shared/constants/torrentStatusMap'; + +import type {Taxonomy} from '../../shared/types/Taxonomy'; +import type {TorrentStatus} from '../../shared/constants/torrentStatusMap'; +import type {TorrentProperties, TorrentList} from '../../shared/types/Torrent'; + +interface TaxonomyServiceEvents { + TAXONOMY_DIFF_CHANGE: (payload: {id: number; diff: Operation[]}) => void; +} + +class TaxonomyService extends BaseService { + taxonomy: Taxonomy = { + statusCounts: {'': 0}, + tagCounts: {'': 0, untagged: 0}, + trackerCounts: {'': 0}, + }; + + lastTaxonomy: Taxonomy = this.taxonomy; + + constructor(...args: ConstructorParameters) { + super(...args); + + this.onServicesUpdated = () => { + if (this.services?.clientGatewayService == null) { + return; + } + + const {clientGatewayService} = this.services; + + clientGatewayService.on('PROCESS_TORRENT_LIST_START', this.handleProcessTorrentListStart); + clientGatewayService.on('PROCESS_TORRENT_LIST_END', this.handleProcessTorrentListEnd); + clientGatewayService.on('PROCESS_TORRENT', this.handleProcessTorrent); + }; + } + + destroy() { + if (this.services?.clientGatewayService == null) { + return; + } + + const {clientGatewayService} = this.services; + + clientGatewayService.removeListener('PROCESS_TORRENT_LIST_START', this.handleProcessTorrentListStart); + clientGatewayService.removeListener('PROCESS_TORRENT_LIST_END', this.handleProcessTorrentListEnd); + clientGatewayService.removeListener('PROCESS_TORRENT', this.handleProcessTorrent); + + super.destroy(); + } + + getTaxonomy() { + return { + id: Date.now(), + taxonomy: this.taxonomy, + }; + } + + handleProcessTorrentListStart = () => { + this.lastTaxonomy = { + statusCounts: {...this.taxonomy.statusCounts}, + tagCounts: {...this.taxonomy.tagCounts}, + trackerCounts: {...this.taxonomy.trackerCounts}, + }; + + torrentStatusMap.forEach((status) => { + this.taxonomy.statusCounts[status] = 0; + }); + + this.taxonomy.statusCounts[''] = 0; + this.taxonomy.tagCounts = {'': 0, untagged: 0}; + this.taxonomy.trackerCounts = {'': 0}; + }; + + handleProcessTorrentListEnd = ({torrents}: {torrents: TorrentList}) => { + const {length} = Object.keys(torrents); + + this.taxonomy.statusCounts[''] = length; + this.taxonomy.tagCounts[''] = length; + this.taxonomy.trackerCounts[''] = length; + + const taxonomyDiffs = jsonpatch.compare(this.lastTaxonomy, this.taxonomy); + + if (taxonomyDiffs.length > 0) { + this.emit('TAXONOMY_DIFF_CHANGE', { + diff: taxonomyDiffs, + id: Date.now(), + }); + } + }; + + handleProcessTorrent = (torrentProperties: TorrentProperties) => { + this.incrementStatusCounts(torrentProperties.status); + this.incrementTagCounts(torrentProperties.tags); + this.incrementTrackerCounts(torrentProperties.trackerURIs); + }; + + incrementStatusCounts(statuses: Array) { + statuses.forEach((status) => { + this.taxonomy.statusCounts[status] += 1; + }); + } + + incrementTagCounts(tags: TorrentProperties['tags']) { + if (tags.length === 0) { + this.taxonomy.tagCounts.untagged += 1; + } + + tags.forEach((tag) => { + if (this.taxonomy.tagCounts[tag] != null) { + this.taxonomy.tagCounts[tag] += 1; + } else { + this.taxonomy.tagCounts[tag] = 1; + } + }); + } + + incrementTrackerCounts(trackers: TorrentProperties['trackerURIs']) { + trackers.forEach((tracker) => { + if (this.taxonomy.trackerCounts[tracker] != null) { + this.taxonomy.trackerCounts[tracker] += 1; + } else { + this.taxonomy.trackerCounts[tracker] = 1; + } + }); + } +} + +export default TaxonomyService; diff --git a/server/services/torrentService.js b/server/services/torrentService.js deleted file mode 100644 index 855b8b871..000000000 --- a/server/services/torrentService.js +++ /dev/null @@ -1,283 +0,0 @@ -const deepEqual = require('deep-equal'); -const BaseService = require('./BaseService'); -const clientGatewayServiceEvents = require('../constants/clientGatewayServiceEvents'); -const config = require('../../config'); -const formatUtil = require('../../shared/util/formatUtil'); -const methodCallUtil = require('../util/methodCallUtil'); -const serverEventTypes = require('../../shared/constants/serverEventTypes'); -const truncateTo = require('../util/numberUtils'); -const torrentListPropMap = require('../constants/torrentListPropMap'); -const torrentServiceEvents = require('../constants/torrentServiceEvents'); -const torrentStatusMap = require('../../shared/constants/torrentStatusMap'); - -const torrentListMethodCallConfig = methodCallUtil.getMethodCallConfigFromPropMap(torrentListPropMap); - -const getTorrentETAFromDetails = torrentDetails => { - const {downRate, bytesDone, sizeBytes} = torrentDetails; - - if (downRate > 0) { - return formatUtil.secondsToDuration((sizeBytes - bytesDone) / downRate); - } - - return Infinity; -}; - -const getTorrentPercentCompleteFromDetails = torrentDetails => { - const percentComplete = (torrentDetails.bytesDone / torrentDetails.sizeBytes) * 100; - - if (percentComplete > 0 && percentComplete < 10) { - return Number(truncateTo(percentComplete, 2)); - } - if (percentComplete > 10 && percentComplete < 100) { - return Number(truncateTo(percentComplete, 1)); - } - - return percentComplete; -}; - -const getTorrentStatusFromDetails = torrentDetails => { - const {isHashChecking, isComplete, isOpen, upRate, downRate, state, message} = torrentDetails; - - const torrentStatus = []; - - if (isHashChecking) { - torrentStatus.push(torrentStatusMap.checking); - } else if (isComplete && isOpen && state === '1') { - torrentStatus.push(torrentStatusMap.complete); - torrentStatus.push(torrentStatusMap.seeding); - } else if (isComplete && isOpen && state === '0') { - torrentStatus.push(torrentStatusMap.stopped); - } else if (isComplete && !isOpen) { - torrentStatus.push(torrentStatusMap.stopped); - torrentStatus.push(torrentStatusMap.complete); - } else if (!isComplete && isOpen && state === '1') { - torrentStatus.push(torrentStatusMap.downloading); - } else if (!isComplete && isOpen && state === '0') { - torrentStatus.push(torrentStatusMap.stopped); - } else if (!isComplete && !isOpen) { - torrentStatus.push(torrentStatusMap.stopped); - } - - if (message.length) { - torrentStatus.push(torrentStatusMap.error); - } - - if (upRate !== 0) { - torrentStatus.push(torrentStatusMap.activelyUploading); - } - - if (downRate !== 0) { - torrentStatus.push(torrentStatusMap.activelyDownloading); - } - - if (upRate !== 0 || downRate !== 0) { - torrentStatus.push(torrentStatusMap.active); - } else { - torrentStatus.push(torrentStatusMap.inactive); - } - - return torrentStatus; -}; - -const hasTorrentFinished = (prevData = {}, nextData = {}) => { - const {status = []} = prevData; - - return ( - !status.includes(torrentStatusMap.checking) && prevData.percentComplete < 100 && nextData.percentComplete === 100 - ); -}; - -class TorrentService extends BaseService { - constructor(...serviceConfig) { - super(...serviceConfig); - - this.errorCount = 0; - this.pollTimeout = null; - this.torrentListSummary = {torrents: {}}; - - this.fetchTorrentList = this.fetchTorrentList.bind(this); - this.handleTorrentProcessed = this.handleTorrentProcessed.bind(this); - this.handleTorrentsRemoved = this.handleTorrentsRemoved.bind(this); - this.handleFetchTorrentListSuccess = this.handleFetchTorrentListSuccess.bind(this); - this.handleFetchTorrentListError = this.handleFetchTorrentListError.bind(this); - - const {clientGatewayService} = this.services; - - clientGatewayService.addTorrentListReducer({ - key: 'status', - reduce: getTorrentStatusFromDetails, - }); - - clientGatewayService.addTorrentListReducer({ - key: 'percentComplete', - reduce: getTorrentPercentCompleteFromDetails, - }); - - clientGatewayService.addTorrentListReducer({ - key: 'eta', - reduce: getTorrentETAFromDetails, - }); - - clientGatewayService.on(clientGatewayServiceEvents.PROCESS_TORRENT, this.handleTorrentProcessed); - - clientGatewayService.on(clientGatewayServiceEvents.TORRENTS_REMOVED, this.handleTorrentsRemoved); - - this.fetchTorrentList(); - } - - assignDeletedTorrentsToDiff(diff, nextTorrentListSummary, options = {}) { - const {newTorrentCount = 0} = options; - - // We need to look for deleted torrents in two scenarios: - // 1. the next list length is less than than the current length - // 2. at least one new torrent was added and the next list length is - // equal to or greater than the current list length. - // - // We definitely don't need to look for deleted torrents if the number - // of new torrents is equal to the difference between next torrent list - // length and previous torrent list length. - let shouldLookForDeletedTorrents = nextTorrentListSummary.length < this.torrentListSummary.length; - - if (newTorrentCount > 0) { - if (nextTorrentListSummary.length >= this.torrentListSummary.length) { - shouldLookForDeletedTorrents = true; - } - - if (newTorrentCount === nextTorrentListSummary.length - this.torrentListSummary.length) { - shouldLookForDeletedTorrents = false; - } - } - - if (shouldLookForDeletedTorrents) { - Object.keys(this.torrentListSummary.torrents).forEach(hash => { - if (nextTorrentListSummary.torrents[hash] == null) { - diff[hash] = { - action: serverEventTypes.TORRENT_LIST_ACTION_TORRENT_DELETED, - }; - } - }, {}); - } - } - - deferFetchTorrentList(interval = config.torrentClientPollInterval || 2000) { - this.pollTimeout = setTimeout(this.fetchTorrentList, interval); - } - - destroy() { - clearTimeout(this.pollTimeout); - } - - fetchTorrentList() { - if (this.pollTimeout != null) { - clearTimeout(this.pollTimeout); - } - - return this.services.clientGatewayService - .fetchTorrentList(torrentListMethodCallConfig) - .then(this.handleFetchTorrentListSuccess) - .catch(this.handleFetchTorrentListError); - } - - getTorrent(hash) { - return this.torrentListSummary.torrents[hash]; - } - - getTorrentList() { - return this.torrentListSummary; - } - - getTorrentListDiff(nextTorrentListSummary) { - let newTorrentCount = 0; - - // Get the diff... - const diff = Object.keys(nextTorrentListSummary.torrents).reduce((accumulator, hash) => { - const currentTorrentDetails = this.torrentListSummary.torrents[hash]; - const nextTorrentDetails = nextTorrentListSummary.torrents[hash]; - - // If the current torrent list doesn't contain any details for this - // hash, then it's a brand new torrent, so every detail is part of the - // diff. - if (currentTorrentDetails == null) { - accumulator[hash] = { - action: serverEventTypes.TORRENT_LIST_ACTION_TORRENT_ADDED, - data: nextTorrentDetails, - }; - - // Track the number of new torrents added. - newTorrentCount++; - } else { - Object.keys(nextTorrentDetails).forEach(propKey => { - // If one of the details is inequal, we need to add it to the diff. - if (!deepEqual(currentTorrentDetails[propKey], nextTorrentDetails[propKey])) { - // Initialize with an empty object when this is the first known - // inequal property. - if (accumulator[hash] == null) { - accumulator[hash] = { - action: serverEventTypes.TORRENT_LIST_ACTION_TORRENT_DETAIL_UPDATED, - data: {}, - }; - } - - // Add the diff details. - accumulator[hash].data[propKey] = nextTorrentDetails[propKey]; - } - }); - } - - return accumulator; - }, {}); - - this.assignDeletedTorrentsToDiff(diff, nextTorrentListSummary, {newTorrentCount}); - - return diff; - } - - handleFetchTorrentListError() { - let nextInterval = config.torrentClientPollInterval || 2000; - - // If more than consecutive errors have occurred, then we delay the next - // request. - if (++this.errorCount >= 3) { - nextInterval = Math.min(nextInterval + 2 ** this.errorCount, 1000 * 60); - } - - this.deferFetchTorrentList(nextInterval); - - this.emit(torrentServiceEvents.FETCH_TORRENT_LIST_ERROR); - } - - getTorrentListSummary() { - return this.torrentListSummary; - } - - handleFetchTorrentListSuccess(nextTorrentListSummary) { - const diff = this.getTorrentListDiff(nextTorrentListSummary); - if (Object.keys(diff).length > 0) { - this.emit(torrentServiceEvents.TORRENT_LIST_DIFF_CHANGE, {diff, id: nextTorrentListSummary.id}); - } - - this.torrentListSummary = nextTorrentListSummary; - - this.deferFetchTorrentList(); - - this.errorCount = 0; - this.emit(torrentServiceEvents.FETCH_TORRENT_LIST_SUCCESS); - } - - handleTorrentProcessed(nextTorrentDetails) { - const prevTorrentDetails = this.torrentListSummary.torrents[nextTorrentDetails.hash]; - - if (hasTorrentFinished(prevTorrentDetails, nextTorrentDetails)) { - this.services.notificationService.addNotification({ - id: 'notification.torrent.finished', - data: {name: nextTorrentDetails.name}, - }); - } - } - - handleTorrentsRemoved() { - this.fetchTorrentList(); - } -} - -module.exports = TorrentService; diff --git a/server/services/torrentService.ts b/server/services/torrentService.ts new file mode 100644 index 000000000..ac662c59d --- /dev/null +++ b/server/services/torrentService.ts @@ -0,0 +1,130 @@ +import jsonpatch, {Operation} from 'fast-json-patch'; + +import type {TorrentProperties, TorrentListSummary} from '@shared/types/Torrent'; + +import BaseService from './BaseService'; +import config from '../../config'; +import {hasTorrentFinished} from '../util/torrentPropertiesUtil'; + +interface TorrentServiceEvents { + FETCH_TORRENT_LIST_SUCCESS: () => void; + FETCH_TORRENT_LIST_ERROR: () => void; + TORRENT_LIST_DIFF_CHANGE: (payload: {id: number; diff: Operation[]}) => void; + newListener: (event: keyof Omit) => void; + removeListener: (event: keyof Omit) => void; +} + +class TorrentService extends BaseService { + pollInterval = config.torrentClientPollIntervalIdle; + pollTimeout: NodeJS.Timeout | null = null; + torrentListSummary: TorrentListSummary = {id: Date.now(), torrents: {}}; + + constructor(...args: ConstructorParameters) { + super(...args); + + this.onServicesUpdated = () => { + if (this.services?.clientGatewayService == null) { + return; + } + + const {clientGatewayService} = this.services; + + clientGatewayService.on('PROCESS_TORRENT', this.handleTorrentProcessed); + + this.fetchTorrentList(); + + // starts polling when the first streaming listener is added + this.on('newListener', (event) => { + if (event === 'TORRENT_LIST_DIFF_CHANGE') { + if (this.pollInterval !== config.torrentClientPollInterval) { + this.pollInterval = config.torrentClientPollInterval; + this.deferFetchTorrentList(); + } + } + }); + + // stops polling when the last streaming listener is removed + this.on('removeListener', (event) => { + if (event === 'TORRENT_LIST_DIFF_CHANGE' && this.listenerCount('TORRENT_LIST_DIFF_CHANGE') === 0) { + this.pollInterval = config.torrentClientPollIntervalIdle; + } + }); + }; + } + + deferFetchTorrentList() { + this.pollTimeout = setTimeout(this.fetchTorrentList, this.pollInterval); + } + + destroy() { + if (this.pollTimeout != null) { + clearTimeout(this.pollTimeout); + } + + super.destroy(); + } + + fetchTorrentList = () => { + if (this.pollTimeout != null) { + clearTimeout(this.pollTimeout); + } + + return ( + this.services?.clientGatewayService + ?.fetchTorrentList() + .then(this.handleFetchTorrentListSuccess) + .catch(this.handleFetchTorrentListError) || Promise.resolve(this.handleFetchTorrentListError()) + ); + }; + + getTorrent(hash: TorrentProperties['hash']) { + return this.torrentListSummary.torrents[hash]; + } + + getTorrentList() { + return this.torrentListSummary.torrents; + } + + getTorrentListSummary() { + return this.torrentListSummary; + } + + handleFetchTorrentListError = () => { + this.deferFetchTorrentList(); + + this.emit('FETCH_TORRENT_LIST_ERROR'); + return null; + }; + + handleFetchTorrentListSuccess = (nextTorrentListSummary: this['torrentListSummary']) => { + const diff = jsonpatch.compare(this.torrentListSummary.torrents, nextTorrentListSummary.torrents); + if (diff.length > 0) { + this.emit('TORRENT_LIST_DIFF_CHANGE', { + diff, + id: nextTorrentListSummary.id, + }); + } + + this.torrentListSummary = nextTorrentListSummary; + + this.deferFetchTorrentList(); + + this.emit('FETCH_TORRENT_LIST_SUCCESS'); + return this.torrentListSummary; + }; + + handleTorrentProcessed = (nextTorrentProperties: TorrentProperties) => { + const prevTorrentProperties = this.torrentListSummary.torrents[nextTorrentProperties.hash]; + + if (hasTorrentFinished(prevTorrentProperties, nextTorrentProperties)) { + this.services?.notificationService.addNotification([ + { + id: 'notification.torrent.finished', + data: {name: nextTorrentProperties.name}, + }, + ]); + } + }; +} + +export default TorrentService; diff --git a/server/tsconfig.json b/server/tsconfig.json new file mode 100644 index 000000000..a11abf474 --- /dev/null +++ b/server/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2019", + "moduleResolution": "node", + "allowJs": true, + "module": "commonjs", + "sourceMap": false, + "strict": true, + "isolatedModules": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "baseUrl": "../", + "paths": { + "@shared/*": ["shared/*"] + }, + "outDir": "../dist" + }, + "include": ["./**/*.ts", "../shared/**/*.ts", "../config.js", "../config.ts"] +} diff --git a/server/util/ajaxUtil.js b/server/util/ajaxUtil.js deleted file mode 100644 index 55f0fdf70..000000000 --- a/server/util/ajaxUtil.js +++ /dev/null @@ -1,21 +0,0 @@ -const ajaxUtil = { - getResponseFn: res => (data, error) => { - if (error) { - if (process.env.NODE_ENV === 'development') { - console.trace(error); - } - - if (typeof error === 'string') { - error = { - message: error, - }; - } - - res.status(500).json(error); - } else { - res.json(data); - } - }, -}; - -module.exports = ajaxUtil; diff --git a/server/util/ajaxUtil.ts b/server/util/ajaxUtil.ts new file mode 100644 index 000000000..306d1fdf0 --- /dev/null +++ b/server/util/ajaxUtil.ts @@ -0,0 +1,25 @@ +import type {Response} from 'express'; + +export const validationError = (res: Response, err: Error) => { + res.status(422).json({ + message: 'Validation error.', + error: err, + }); +}; + +export const getResponseFn = (res: Response) => (data: D, error?: Error | string) => { + if (error) { + if (process.env.NODE_ENV === 'development') { + console.trace(error); + } + + if (typeof error === 'string') { + res.status(500).json(Error(error)); + return; + } + + res.status(500).json(error); + } else { + res.json(data); + } +}; diff --git a/server/util/clientResponseUtil.js b/server/util/clientResponseUtil.js deleted file mode 100644 index 99c65458f..000000000 --- a/server/util/clientResponseUtil.js +++ /dev/null @@ -1,127 +0,0 @@ -const geoip = require('geoip-country'); -const truncateTo = require('./numberUtils'); -const torrentFilePropsMap = require('../../shared/constants/torrentFilePropsMap'); -const torrentPeerPropsMap = require('../../shared/constants/torrentPeerPropsMap'); -const torrentTrackerPropsMap = require('../../shared/constants/torrentTrackerPropsMap'); - -const processFile = file => { - file.filename = file.pathComponents[file.pathComponents.length - 1]; - file.percentComplete = truncateTo((file.completedChunks / file.sizeChunks) * 100); - - delete file.completedChunks; - delete file.pathComponents; - delete file.sizeChunks; - - return file; -}; - -const getFileTreeFromPathsArr = (tree, directory, file, depth) => { - if (depth == null) { - depth = 0; - } - - if (tree == null) { - tree = {}; - } - - if (depth++ < file.pathComponents.length - 1) { - if (!tree.directories) { - tree.directories = {}; - } - - tree.directories[directory] = getFileTreeFromPathsArr( - tree.directories[directory], - file.pathComponents[depth], - file, - depth, - ); - } else { - if (!tree.files) { - tree.files = []; - } - - tree.files.push(processFile(file)); - } - - return tree; -}; - -const mapPropsToResponse = (requestedKeys, clientResponse) => { - if (clientResponse.length === 0) { - return []; - } - - // clientResponse is always an array of arrays. - if (clientResponse[0].length === 1) { - // When the length of the nested arrays is 1, the nested arrays represent a - // singular requested value (e.g. total data transferred or current upload - // speed). Therefore we construct an object where the requested keys map to - // their values. - return clientResponse.reduce((memo, value, index) => { - const singleValue = value[0]; - memo[requestedKeys[index]] = singleValue; - - return memo; - }, {}); - } - // When the length of the nested arrays is more than 1, the nested arrays - // represent one of many items of the same type (e.g. a list of torrents, - // peers, files, etc). Therefore we construct an array of objects, where each - // object contains all of the requested keys and its value. We add an index - // for each item, a requirement for file lists. - return clientResponse.map( - (listItem, index) => - listItem.reduce( - (nestedMemo, value, nestedIndex) => { - nestedMemo[requestedKeys[nestedIndex]] = value; - - return nestedMemo; - }, - {index}, - ), - [], - ); -}; - -const processTorrentDetails = data => { - // TODO: This is ugly. - const peersData = data[0][0] || null; - const filesData = data[1][0] || null; - const trackerData = data[2][0] || null; - let peers = null; - let files = null; - let trackers = null; - let fileTree = {}; - - if (peersData && peersData.length) { - peers = mapPropsToResponse(torrentPeerPropsMap.props, peersData).map(peer => { - const geoData = geoip.lookup(peer.address) || {}; - peer.country = geoData.country; - - // Strings to boolean - peer.isEncrypted = peer.isEncrypted === '1'; - peer.isIncoming = peer.isIncoming === '1'; - - return peer; - }); - } - - if (filesData && filesData.length) { - files = mapPropsToResponse(torrentFilePropsMap.props, filesData); - fileTree = files.reduce((memo, file) => getFileTreeFromPathsArr(memo, file.pathComponents[0], file), {}); - } - - if (trackerData && trackerData.length) { - trackers = mapPropsToResponse(torrentTrackerPropsMap.props, trackerData); - } - - return {peers, trackers, fileTree}; -}; - -const clientResponseUtil = { - mapPropsToResponse, - processFile, - processTorrentDetails, -}; - -module.exports = clientResponseUtil; diff --git a/server/util/diskUsageUtil.ts b/server/util/diskUsageUtil.ts new file mode 100644 index 000000000..73de5c017 --- /dev/null +++ b/server/util/diskUsageUtil.ts @@ -0,0 +1,107 @@ +import {execFile} from 'child_process'; +import util from 'util'; + +import type {Disk} from '@shared/types/DiskUsage'; + +import config from '../../config'; + +const execFileAsync = util.promisify(execFile); + +const PLATFORMS_SUPPORTED = ['darwin', 'linux', 'freebsd', 'win32'] as const; +export type SupportedPlatform = Extract; + +export const isPlatformSupported = (): boolean => { + return PLATFORMS_SUPPORTED.includes(process.platform as SupportedPlatform); +}; + +const filterMountPoint = (mountpoint: string) => { + const {watchMountPoints} = config; + + if (watchMountPoints != null) { + // if user has configured watchMountPoints, filter each line output for given array + return watchMountPoints.includes(mountpoint); + } + + // include all mounted file systems by default + return true; +}; + +const MAX_BUFFER_SIZE = 65536; +export const diskUsage: Readonly Promise>>> = { + linux: () => + execFileAsync('df -T | tail -n+2', { + shell: true, + maxBuffer: MAX_BUFFER_SIZE, + }).then(({stdout}) => + stdout + .trim() + .split('\n') + .map((disk) => disk.split(/\s+/)) + .filter((disk) => filterMountPoint(disk[6])) + .filter((disk) => disk[1] !== 'devtmpfs' && disk[1] !== 'squashfs' && disk[1] !== 'tmpfs') + .map(([_dev, _fs, size, used, avail, _pcent, target]) => { + return { + size: Number.parseInt(size, 10) * 1024, + used: Number.parseInt(used, 10) * 1024, + avail: Number.parseInt(avail, 10) * 1024, + target, + }; + }), + ), + freebsd: () => + execFileAsync('df | tail -n+2', { + shell: true, + maxBuffer: MAX_BUFFER_SIZE, + }).then(({stdout}) => + stdout + .trim() + .split('\n') + .map((disk) => disk.split(/\s+/)) + .filter((disk) => filterMountPoint(disk[5])) + .map(([_dev, size, used, avail, _pcent, target]) => { + return { + size: Number.parseInt(size, 10) * 1024, + used: Number.parseInt(used, 10) * 1024, + avail: Number.parseInt(avail, 10) * 1024, + target, + }; + }), + ), + darwin: () => + execFileAsync('df -kl | tail -n+2', { + shell: true, + maxBuffer: MAX_BUFFER_SIZE, + }).then(({stdout}) => + stdout + .trim() + .split('\n') + .map((disk) => disk.split(/\s+/)) + .filter((disk) => filterMountPoint(disk[8])) + .map(([_dev, size, used, avail, _pcent, _iused, _ifree, _piused, target]) => { + return { + size: Number.parseInt(size, 10) * 1024, + used: Number.parseInt(used, 10) * 1024, + avail: Number.parseInt(avail, 10) * 1024, + target, + }; + }), + ), + win32: () => + execFileAsync('wmic logicaldisk', { + shell: true, + maxBuffer: MAX_BUFFER_SIZE, + }).then(({stdout}) => + stdout + .trim() + .split('\n') + .slice(1) + .map((disk) => disk.split(/\s+/)) + .filter((disk) => filterMountPoint(disk[1])) + .map((disk) => ({ + size: Number(disk[14]), + used: Number(disk[14]) - Number(disk[10]), + avail: Number(disk[10]), + target: disk[1], + })), + ), +}; diff --git a/server/util/feedUtil.ts b/server/util/feedUtil.ts new file mode 100644 index 000000000..42a9cb294 --- /dev/null +++ b/server/util/feedUtil.ts @@ -0,0 +1,89 @@ +import type {FeedItem} from 'feedsub'; + +import {cdata as matchCDATA} from '../../shared/util/regEx'; + +import type {AddTorrentByURLOptions} from '../../shared/schema/api/torrents'; +import type {Rule} from '../../shared/types/Feed'; + +interface PendingDownloadItems + extends Required> { + matchTitle: string; + ruleID: string; + ruleLabel: string; +} + +interface RSSEnclosure { + url?: string; + length?: number; + type?: string; +} + +// TODO: Allow users to specify which key contains the URLs. +export const getTorrentUrlsFromFeedItem = (feedItem: FeedItem): Array => { + // If we've got an Array of enclosures, we'll iterate over the values and + // look for the url key. + const RSSEnclosures = (feedItem.enclosures as unknown) as Array | undefined; + if (RSSEnclosures && Array.isArray(RSSEnclosures)) { + return RSSEnclosures.reduce((urls: Array, enclosure) => { + if (typeof enclosure.url === 'string') { + urls.push(enclosure.url); + } + + return urls; + }, []); + } + + // If we've got a Object of enclosures, use url key + const RSSEnclosure = feedItem.enclosure as RSSEnclosure | undefined; + if (typeof RSSEnclosure?.url === 'string') { + return [RSSEnclosure.url]; + } + + // If there are no enclosures, then use the link tag instead + if (feedItem.link) { + // remove CDATA tags around links + const cdata = matchCDATA.exec(feedItem.link as string); + + if (cdata && cdata[1]) { + return [cdata[1]]; + } + + return [feedItem.link as string]; + } + + return []; +}; + +export const getFeedItemsMatchingRules = ( + feedItems: Array, + rules: Array, +): Array => { + return feedItems.reduce((matchedItems: Array, feedItem) => { + rules.forEach((rule) => { + const matchField = rule.field ? (feedItem[rule.field] as string) : (feedItem.title as string); + const isMatched = new RegExp(rule.match, 'gi').test(matchField); + const isExcluded = rule.exclude !== '' && new RegExp(rule.exclude, 'gi').test(matchField); + + if (isMatched && !isExcluded) { + const torrentUrls = getTorrentUrlsFromFeedItem(feedItem); + const isAlreadyDownloaded = matchedItems.some((matchedItem) => + torrentUrls.every((url) => matchedItem.urls.includes(url)), + ); + + if (!isAlreadyDownloaded && torrentUrls[0] != null) { + matchedItems.push({ + urls: torrentUrls as [string, ...string[]], + tags: rule.tags, + matchTitle: feedItem.title as string, + ruleID: rule._id, + ruleLabel: rule.label, + destination: rule.destination, + start: rule.startOnLoad, + }); + } + } + }); + + return matchedItems; + }, []); +}; diff --git a/server/util/fileUtil.ts b/server/util/fileUtil.ts new file mode 100644 index 000000000..26b305cf2 --- /dev/null +++ b/server/util/fileUtil.ts @@ -0,0 +1,91 @@ +import fs from 'fs'; +import {homedir} from 'os'; +import path from 'path'; + +import config from '../../config'; + +export const accessDeniedError = () => { + const error = new Error() as NodeJS.ErrnoException; + error.code = 'EACCES'; + return error; +}; + +export const fileNotFoundError = () => { + const error = new Error() as NodeJS.ErrnoException; + error.code = 'ENOENT'; + return error; +}; + +export const isAllowedPath = (resolvedPath: string) => { + if (config.allowedPaths == null) { + return true; + } + + let realPath: string | null = null; + let parentPath: string = resolvedPath; + while (realPath == null) { + try { + realPath = fs.realpathSync(parentPath); + } catch (e) { + if (e.code === 'ENOENT') { + parentPath = path.resolve(parentPath, '..'); + } else { + return false; + } + } + } + + return config.allowedPaths.some((allowedPath) => { + if (realPath?.startsWith(allowedPath)) { + return true; + } + return false; + }); +}; + +export const sanitizePath = (input?: string): string => { + if (typeof input !== 'string') { + throw accessDeniedError(); + } + + // eslint-disable-next-line no-control-regex + const controlRe = /[\x00-\x1f\x80-\x9f]/g; + return path.resolve(input).replace(controlRe, ''); +}; + +export const getDirectoryList = async (inputPath: string) => { + if (typeof inputPath !== 'string') { + throw fileNotFoundError(); + } + + const sourcePath = inputPath.replace(/^~/, homedir()); + + const resolvedPath = sanitizePath(sourcePath); + if (!isAllowedPath(resolvedPath)) { + throw accessDeniedError(); + } + + const directories: Array = []; + const files: Array = []; + + fs.readdirSync(resolvedPath).forEach((item) => { + const joinedPath = path.join(resolvedPath, item); + if (fs.existsSync(joinedPath)) { + if (fs.statSync(joinedPath).isDirectory()) { + directories.push(item); + } else { + files.push(item); + } + } + }); + + const hasParent = /^.{0,}:?(\/|\\){1,1}\S{1,}/.test(resolvedPath); + + return { + directories, + files, + hasParent, + path: resolvedPath, + separator: path.sep, + }; +}; diff --git a/server/util/mediainfo.js b/server/util/mediainfo.js deleted file mode 100644 index 52922b568..000000000 --- a/server/util/mediainfo.js +++ /dev/null @@ -1,38 +0,0 @@ -const childProcess = require('child_process'); - -const services = require('../services'); - -module.exports = { - getMediainfo(user, options, callback) { - const torrentService = services.getTorrentService(user); - const {hash} = options; - - if (hash == null) { - callback(null, {error: 'Hash must be defined'}); - return; - } - const selectedTorrent = torrentService.getTorrent(hash); - try { - childProcess.execFile( - 'mediainfo', - [selectedTorrent.basePath], - {maxBuffer: 1024 * 2000}, - (error, stdout, stderr) => { - if (error) { - callback(null, {error}); - return; - } - - if (stderr) { - callback(null, {error: stderr}); - return; - } - - callback({output: stdout}); - }, - ); - } catch (childProcessError) { - callback(null, {error: childProcessError}); - } - }, -}; diff --git a/server/util/methodCallUtil.js b/server/util/methodCallUtil.js deleted file mode 100644 index 2aa147331..000000000 --- a/server/util/methodCallUtil.js +++ /dev/null @@ -1,28 +0,0 @@ -const methodCallUtil = { - getMethodCallConfigFromPropMap(map = new Map(), requestedKeys) { - let desiredKeys = Array.from(map.keys()); - - if (requestedKeys != null) { - desiredKeys = desiredKeys.filter(key => requestedKeys.includes(key)); - } - - return desiredKeys.reduce( - (accumulator, key) => { - const {methodCall, transformValue} = map.get(key); - - accumulator.methodCalls.push(methodCall); - accumulator.propLabels.push(key); - accumulator.valueTransformations.push(transformValue); - - return accumulator; - }, - { - methodCalls: [], - propLabels: [], - valueTransformations: [], - }, - ); - }, -}; - -module.exports = methodCallUtil; diff --git a/server/util/minifyUtil.js b/server/util/minifyUtil.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/util/numberUtils.js b/server/util/numberUtils.js deleted file mode 100644 index f47d32302..000000000 --- a/server/util/numberUtils.js +++ /dev/null @@ -1,6 +0,0 @@ -const truncateTo = (num, precision = 0) => { - const factor = 10 ** precision; - return Math.floor(num * factor) / factor; -}; - -module.exports = truncateTo; diff --git a/server/util/rTorrentPropMap.js b/server/util/rTorrentPropMap.js deleted file mode 100644 index 7eed427b0..000000000 --- a/server/util/rTorrentPropMap.js +++ /dev/null @@ -1,12 +0,0 @@ -const RTORRENT_PROPS_MAP = { - transferData: { - uploadRate: 'throttle.global_up.rate', - uploadTotal: 'throttle.global_up.total', - uploadThrottle: 'throttle.global_up.max_rate', - downloadRate: 'throttle.global_down.rate', - downloadTotal: 'throttle.global_down.total', - downloadThrottle: 'throttle.global_down.max_rate', - }, -}; - -module.exports = RTORRENT_PROPS_MAP; diff --git a/server/util/tempFileUtil.ts b/server/util/tempFileUtil.ts new file mode 100644 index 000000000..7954caea4 --- /dev/null +++ b/server/util/tempFileUtil.ts @@ -0,0 +1,80 @@ +import axios, {AxiosError, AxiosResponse} from 'axios'; +import crypto from 'crypto'; +import fs, {WriteFileOptions} from 'fs'; + +import {getTempPath} from '../models/TemporaryStorage'; + +/** + * Gets a randomly generated file path for temp file. + * + * @return {string} - path + */ +const getTempFilePath = (extension = 'tmp'): string => { + return getTempPath(`${Date.now()}-${crypto.randomBytes(8).toString('hex')}.${extension}`); +}; + +/** + * Deletes the file after 5 minutes + */ +const delayedDelete = (tempPath: string): void => { + setTimeout(() => { + try { + fs.unlinkSync(tempPath); + } catch { + // do nothing. + } + }, 1000 * 60 * 5); +}; + +/** + * Saves buffer to temporary storage as a file. + * + * @param {Buffer} buffer - buffer + * @param {string} extension - file extension of temp file + * @return {string} - path of saved temporary file. deleted after 5 minutes. + */ +export const saveBufferToTempFile = async ( + buffer: Buffer, + extension?: string, + options?: WriteFileOptions, +): Promise => { + const tempPath = getTempFilePath(extension); + + fs.writeFileSync(tempPath, buffer, options); + + delayedDelete(tempPath); + + return tempPath; +}; + +/** + * Fetches from URL to temporary storage. + * + * @param {string} url - URL + * @param {string} extension - file extension of temp file + * @return {string} - path of saved temporary file. deleted after 5 minutes. + */ +export const fetchURLToTempFile = async (url: string, cookies?: Array, extension?: string): Promise => { + const tempPath = getTempFilePath(extension); + + return new Promise((resolve, reject) => { + axios({ + method: 'GET', + url, + responseType: 'stream', + headers: cookies + ? { + Cookie: cookies.join('; ').concat(';'), + } + : undefined, + }).then( + (res: AxiosResponse) => { + delayedDelete(tempPath); + res.data.pipe(fs.createWriteStream(tempPath)).on('finish', () => resolve(tempPath)); + }, + (e: AxiosError) => { + reject(e); + }, + ); + }); +}; diff --git a/server/util/torrentFileUtil.ts b/server/util/torrentFileUtil.ts new file mode 100644 index 000000000..0afc32199 --- /dev/null +++ b/server/util/torrentFileUtil.ts @@ -0,0 +1,148 @@ +import bencode from 'bencode'; +import fs from 'fs'; +import path from 'path'; + +import {LibTorrentFilePriority} from '../../shared/types/TorrentFile'; + +import type {LibTorrentResume, RTorrentFile, TorrentFile} from '../../shared/types/TorrentFile'; + +const openAndDecodeTorrent = async (torrentPath: string): Promise => { + let torrentData: TorrentFile | null = null; + + try { + torrentData = bencode.decode(fs.readFileSync(torrentPath)); + } catch { + return null; + } + + if (torrentData == null) { + return null; + } + + return torrentData; +}; + +export const getContentSize = async (info: TorrentFile['info']): Promise => { + if (info.length != null) { + // Single file torrent + return info.length; + } + + if (info.files != null) { + // Multi file torrent + let totalLength = 0; + info.files.forEach(({length}) => { + totalLength += length; + }); + return totalLength; + } + + // Things are not right + return 0; +}; + +export const setTrackers = async (torrent: string, trackers: Array): Promise => { + const torrentData = await openAndDecodeTorrent(torrent); + + if (torrentData == null) { + return false; + } + + if (trackers.length > 1 || torrentData['announce-list'] != null) { + torrentData['announce-list'] = []; + torrentData['announce-list'].push( + trackers.map((tracker) => { + return Buffer.from(tracker); + }), + ); + } + + try { + fs.writeFileSync(torrent, bencode.encode(torrentData)); + } catch { + return false; + } + + return true; +}; + +export const setCompleted = async (torrent: string, destination: string, isBasePath = true): Promise => { + const torrentData = await openAndDecodeTorrent(torrent); + + if (torrentData == null) { + return false; + } + + const {info} = torrentData; + if (info == null) { + return false; + } + + const contentSize = await getContentSize(info); + const pieceSize = Number(info['piece length']); + if (contentSize === 0 || pieceSize == null || pieceSize === 0) { + return false; + } + + const contentPathsWithLengths: Array<[string, number]> = []; + + if (info.length != null) { + // Single file torrent + contentPathsWithLengths.push([path.resolve(path.join(destination, info.name.toString())), info.length]); + } else if (info.files != null) { + // Multi file torrent + const basePath = isBasePath ? destination : path.join(destination, info.name.toString()); + info.files.forEach((content) => { + contentPathsWithLengths.push([ + path.resolve(path.join(basePath, content.path.map((pathBuffer) => pathBuffer.toString()).join('/'))), + content.length, + ]); + }); + } else { + return false; + } + + const completedFileResumeTree: LibTorrentResume['files'] = contentPathsWithLengths.map((contentPathWithLength) => { + const [contentPath, contentLength] = contentPathWithLength; + + if (!fs.existsSync(contentPath)) { + return { + completed: 0, + mtime: 0, + priority: LibTorrentFilePriority.NORMAL, + }; + } + + const fileStat = fs.lstatSync(contentPath); + if (!fileStat.isFile() || fileStat.size !== contentLength) { + return { + completed: 0, + mtime: 0, + priority: LibTorrentFilePriority.OFF, + }; + } + + return { + completed: Math.ceil(contentLength / pieceSize), + mtime: Math.trunc(fileStat.mtimeMs / 1000), + priority: LibTorrentFilePriority.OFF, + }; + }); + + const completedResume: LibTorrentResume = { + bitfield: Math.ceil(contentSize / pieceSize), + files: completedFileResumeTree, + }; + + const torrentDataWithResume: RTorrentFile = Object.assign(torrentData, { + libtorrent_resume: completedResume, + }); + + try { + fs.writeFileSync(torrent, bencode.encode(torrentDataWithResume)); + } catch { + return false; + } + + return true; +}; diff --git a/server/util/torrentPropertiesUtil.ts b/server/util/torrentPropertiesUtil.ts new file mode 100644 index 000000000..b6e8e2a17 --- /dev/null +++ b/server/util/torrentPropertiesUtil.ts @@ -0,0 +1,52 @@ +import {domainName as matchDomainName} from '../../shared/util/regEx'; + +import type {TorrentProperties} from '../../shared/types/Torrent'; + +export const hasTorrentFinished = ( + prevData: Partial = {}, + nextData: Partial = {}, +): boolean => { + if (prevData.status != null && prevData.status.includes('checking')) { + return false; + } + + if (prevData.percentComplete == null || nextData.percentComplete == null) { + return false; + } + + if (prevData.percentComplete < 100 && nextData.percentComplete === 100) { + return true; + } + + return false; +}; + +export const getDomainsFromURLs = (urls: Array): Array => { + const domains: Array = []; + + urls.forEach((url) => { + const regexMatched = matchDomainName.exec(url); + + if (regexMatched != null && regexMatched[1]) { + let domain = regexMatched[1]; + + const minSubsetLength = 3; + const domainSubsets = domain.split('.'); + let desiredSubsets = 2; + + if (domainSubsets.length > desiredSubsets) { + const lastDesiredSubset = domainSubsets[domainSubsets.length - desiredSubsets]; + if (lastDesiredSubset.length <= minSubsetLength) { + desiredSubsets += 1; + } + } + + domain = domainSubsets.slice(desiredSubsets * -1).join('.'); + + domains.push(domain); + } + }); + + // Deduplicate + return [...new Set(domains)]; +}; diff --git a/server/views/error.pug b/server/views/error.pug deleted file mode 100644 index 51ec12c6a..000000000 --- a/server/views/error.pug +++ /dev/null @@ -1,6 +0,0 @@ -extends layout - -block content - h1= message - h2= error.status - pre #{error.stack} diff --git a/server/views/layout.pug b/server/views/layout.pug deleted file mode 100644 index 25905480a..000000000 --- a/server/views/layout.pug +++ /dev/null @@ -1,6 +0,0 @@ -doctype html -html - head - title= title - body - block content diff --git a/shared/config/paths.d.ts b/shared/config/paths.d.ts new file mode 100644 index 000000000..cc397b3c7 --- /dev/null +++ b/shared/config/paths.d.ts @@ -0,0 +1,15 @@ +declare const PATHS: { + appBuild: string; + appDist: string; + appPublic: string; + appHtml: string; + appIndex: string; + appPackageJson: string; + appSrc: string; + clientSrc: string; + testsSetup: string; + appNodeModules: string; + servedPath: string; +}; + +export = PATHS; diff --git a/shared/config/paths.js b/shared/config/paths.js new file mode 100644 index 000000000..33a54613b --- /dev/null +++ b/shared/config/paths.js @@ -0,0 +1,47 @@ +const fs = require('fs'); +const path = require('path'); +const userConfig = require('../../config'); + +// Make sure any symlinks in the project folder are resolved: +// https://github.com/facebookincubator/create-react-app/issues/637 +const appDirectory = path.resolve(path.join(__dirname, '../..')); +const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath); +const ensureSlash = (questionablePath, needsSlash) => { + const hasSlash = questionablePath.endsWith('/'); + if (hasSlash && !needsSlash) { + return questionablePath.substr(questionablePath, questionablePath.length - 1); + } + if (!hasSlash && needsSlash) { + return `${questionablePath}/`; + } + return questionablePath; +}; + +const getAppDist = () => { + // In production, assets are in assets/. + const appDist = path.resolve(path.join(__dirname, 'assets')); + if (!fs.existsSync(appDist)) { + // In development, assets are in ${appDirectory}/dist/assets/. + const appBuild = resolveApp('dist/assets'); + if (fs.existsSync(appBuild)) { + return appBuild; + } + } + return appDist; +}; + +const PATHS = { + appBuild: resolveApp('dist/assets'), + appDist: getAppDist(), + appPublic: resolveApp('client/src/public/'), + appHtml: resolveApp('client/src/index.html'), + appIndex: resolveApp('client/src/javascript/app.tsx'), + appPackageJson: resolveApp('package.json'), + appSrc: resolveApp('./'), + clientSrc: resolveApp('client/src'), + testsSetup: resolveApp('tests/setupTests.js'), + appNodeModules: resolveApp('node_modules'), + servedPath: ensureSlash(userConfig.baseURI || '/', true), +}; + +module.exports = PATHS; diff --git a/shared/constants/clientSettingsMap.js b/shared/constants/clientSettingsMap.js deleted file mode 100644 index 94bc0ce33..000000000 --- a/shared/constants/clientSettingsMap.js +++ /dev/null @@ -1,101 +0,0 @@ -const objectUtil = require('../util/objectUtil'); - -const clientSettingsMap = objectUtil.reflect({ - dhtPort: 'dht.port', - dhtStats: 'dht.statistics', - directoryDefault: 'directory.default', - maxFileSize: 'system.file.max_size', - networkBindAddress: 'network.bind_address', - networkHttpCert: 'network.http.cacert', - networkHttpMaxOpen: 'network.http.max_open', - networkHttpPath: 'network.http.capath', - networkHttpProxy: 'network.http.proxy_address', - networkLocalAddress: 'network.local_address', - networkMaxOpenFiles: 'network.max_open_files', - networkMaxOpenSockets: 'network.max_open_sockets', - networkPortOpen: 'network.port_open', - networkPortRandom: 'network.port_random', - networkPortRange: 'network.port_range', - networkReceiveBufferSize: 'network.receive_buffer.size', - networkScgiDontRoute: 'network.scgi.dont_route', - networkSendBufferSize: 'network.send_buffer.size', - piecesHashOnCompletion: 'pieces.hash.on_completion', - piecesMemoryMax: 'pieces.memory.max', - piecesPreloadMinRate: 'pieces.preload.min_rate', - piecesPreloadMinSize: 'pieces.preload.min_size', - piecesPreloadType: 'pieces.preload.type', - piecesSyncAlwaysSafe: 'pieces.sync.always_safe', - piecesSyncTimeout: 'pieces.sync.timeout', - piecesSyncTimeoutSafe: 'pieces.sync.timeout_safe', - protocolPex: 'protocol.pex', - sessionOnCompletion: 'session.on_completion', - sessionPath: 'session.path', - sessionUseLock: 'session.use_lock', - systemFileSplitSize: 'system.file.split_size', - systemFileSplitSuffix: 'system.file.split_suffix', - throttleGlobalDownMax: 'throttle.global_down.max_rate', - throttleGlobalUpMax: 'throttle.global_up.max_rate', - throttleMaxPeersNormal: 'throttle.max_peers.normal', - throttleMaxPeersSeed: 'throttle.max_peers.seed', - throttleMaxDownloads: 'throttle.max_downloads', - throttleMaxDownloadsDiv: 'throttle.max_downloads.div', - throttleMaxDownloadsGlobal: 'throttle.max_downloads.global', - throttleMaxUploads: 'throttle.max_uploads', - throttleMaxUploadsDiv: 'throttle.max_uploads.div', - throttleMaxUploadsGlobal: 'throttle.max_uploads.global', - throttleMinPeersNormal: 'throttle.min_peers.normal', - throttleMinPeersSeed: 'throttle.min_peers.seed', - trackersNumWant: 'trackers.numwant', - trackersUseUdp: 'trackers.use_udp', -}); - -clientSettingsMap.defaults = [ - 'dhtPort', - 'dhtStats', - 'directoryDefault', - 'maxFileSize', - 'networkBindAddress', - 'networkHttpCert', - 'networkHttpMaxOpen', - 'networkHttpPath', - 'networkHttpProxy', - 'networkLocalAddress', - 'networkMaxOpenFiles', - 'networkMaxOpenSockets', - 'networkPortOpen', - 'networkPortRandom', - 'networkPortRange', - 'networkReceiveBufferSize', - 'networkScgiDontRoute', - 'networkSendBufferSize', - 'piecesHashOnCompletion', - 'piecesMemoryMax', - 'piecesPreloadMinRate', - 'piecesPreloadMinSize', - 'piecesPreloadType', - 'piecesSyncAlwaysSafe', - 'piecesSyncTimeout', - 'piecesSyncTimeoutSafe', - 'protocolPex', - 'sessionOnCompletion', - 'sessionPath', - 'sessionUseLock', - 'systemFileSplitSize', - 'systemFileSplitSuffix', - 'throttleGlobalDownMax', - 'throttleGlobalUpMax', - 'throttleMaxDownloadsDiv', - 'throttleMaxDownloadsGlobal', - 'throttleMaxPeersNormal', - 'throttleMaxPeersSeed', - 'throttleMaxDownloads', - 'throttleMaxUploads', - 'throttleMaxUploadsDiv', - 'throttleMaxUploadsGlobal', - 'throttleMinPeersNormal', - 'throttleMinPeersSeed', - 'trackersNumWant', - 'trackersUseUdp', -]; - -module.exports = clientSettingsMap; diff --git a/shared/constants/defaultFloodSettings.ts b/shared/constants/defaultFloodSettings.ts new file mode 100644 index 000000000..52252ba62 --- /dev/null +++ b/shared/constants/defaultFloodSettings.ts @@ -0,0 +1,75 @@ +import type {FloodSettings} from '@shared/types/FloodSettings'; + +const defaultFloodSettings: Readonly = { + language: 'auto', + sortTorrents: { + direction: 'desc', + property: 'dateAdded', + }, + torrentListColumns: [ + {id: 'name', visible: true}, + {id: 'percentComplete', visible: true}, + {id: 'downTotal', visible: true}, + {id: 'downRate', visible: true}, + {id: 'upTotal', visible: true}, + {id: 'upRate', visible: true}, + {id: 'eta', visible: true}, + {id: 'ratio', visible: true}, + {id: 'sizeBytes', visible: true}, + {id: 'peers', visible: true}, + {id: 'seeds', visible: true}, + {id: 'dateAdded', visible: true}, + {id: 'dateCreated', visible: false}, + {id: 'directory', visible: false}, + {id: 'hash', visible: false}, + {id: 'isPrivate', visible: false}, + {id: 'message', visible: false}, + {id: 'trackerURIs', visible: false}, + {id: 'tags', visible: true}, + ], + torrentListColumnWidths: { + name: 200, + percentComplete: 100, + downTotal: 100, + downRate: 100, + upTotal: 100, + upRate: 100, + eta: 100, + ratio: 100, + sizeBytes: 100, + peers: 100, + seeds: 100, + dateAdded: 100, + dateCreated: 100, + directory: 100, + hash: 100, + isPrivate: 100, + message: 100, + trackerURIs: 100, + tags: 100, + }, + torrentContextMenuActions: [ + {id: 'start', visible: true}, + {id: 'stop', visible: true}, + {id: 'remove', visible: true}, + {id: 'checkHash', visible: true}, + {id: 'setTaxonomy', visible: true}, + {id: 'move', visible: true}, + {id: 'setTrackers', visible: false}, + {id: 'torrentDetails', visible: true}, + {id: 'torrentDownload', visible: true}, + {id: 'generateMagnet', visible: false}, + {id: 'setPriority', visible: false}, + ], + torrentListViewSize: 'condensed', + speedLimits: { + // B/s + download: [1024, 10240, 102400, 512000, 1048576, 2097152, 5242880, 10485760, 0], + upload: [1024, 10240, 102400, 512000, 1048576, 2097152, 5242880, 10485760, 0], + }, + mountPoints: [], + deleteTorrentData: true, + startTorrentsOnLoad: true, +}; + +export default defaultFloodSettings; diff --git a/shared/constants/diffActionTypes.js b/shared/constants/diffActionTypes.js deleted file mode 100644 index 4c5efd578..000000000 --- a/shared/constants/diffActionTypes.js +++ /dev/null @@ -1,7 +0,0 @@ -const diffActionTypes = ['ITEM_ADDED', 'ITEM_CHANGED', 'ITEM_REMOVED']; - -module.exports = diffActionTypes.reduce((memo, key) => { - memo[key] = key; - - return memo; -}, {}); diff --git a/shared/constants/historySnapshotTypes.js b/shared/constants/historySnapshotTypes.js deleted file mode 100644 index 88de58ce2..000000000 --- a/shared/constants/historySnapshotTypes.js +++ /dev/null @@ -1,12 +0,0 @@ -const objectUtil = require('../util/objectUtil'); - -const historySnapshotTypes = { - FIVE_MINUTE: 'fiveMin', - THIRTY_MINUTE: 'thirtyMin', - HOUR: 'hour', - WEEK: 'week', - MONTH: 'month', - YEAR: 'year', -}; - -module.exports = objectUtil.reflect(historySnapshotTypes); diff --git a/shared/constants/historySnapshotTypes.ts b/shared/constants/historySnapshotTypes.ts new file mode 100644 index 000000000..89f0f0db0 --- /dev/null +++ b/shared/constants/historySnapshotTypes.ts @@ -0,0 +1,4 @@ +const historySnapshotTypes = ['FIVE_MINUTE', 'THIRTY_MINUTE', 'HOUR', 'DAY', 'WEEK', 'MONTH', 'YEAR'] as const; + +export default historySnapshotTypes; +export type HistorySnapshot = typeof historySnapshotTypes[number]; diff --git a/shared/constants/serverEventTypes.js b/shared/constants/serverEventTypes.js deleted file mode 100644 index 5843b3a38..000000000 --- a/shared/constants/serverEventTypes.js +++ /dev/null @@ -1,19 +0,0 @@ -const objectUtil = require('../util/objectUtil'); - -const serverEventTypes = [ - 'CLIENT_CONNECTIVITY_STATUS_CHANGE', - 'DISK_USAGE_CHANGE', - 'NOTIFICATION_COUNT_CHANGE', - 'TAXONOMY_FULL_UPDATE', - 'TAXONOMY_DIFF_CHANGE', - 'TORRENT_LIST_ACTION_TORRENT_ADDED', - 'TORRENT_LIST_ACTION_TORRENT_DELETED', - 'TORRENT_LIST_ACTION_TORRENT_DETAIL_UPDATED', - 'TORRENT_LIST_DIFF_CHANGE', - 'TORRENT_LIST_FULL_UPDATE', - 'TRANSFER_HISTORY_FULL_UPDATE', - 'TRANSFER_SUMMARY_DIFF_CHANGE', - 'TRANSFER_SUMMARY_FULL_UPDATE', -]; - -module.exports = objectUtil.createStringMapFromArray(serverEventTypes); diff --git a/shared/constants/torrentFilePropsMap.js b/shared/constants/torrentFilePropsMap.js deleted file mode 100644 index 322dbcdf2..000000000 --- a/shared/constants/torrentFilePropsMap.js +++ /dev/null @@ -1,6 +0,0 @@ -const torrentFilePropsMap = { - props: ['path', 'pathComponents', 'priority', 'sizeBytes', 'sizeChunks', 'completedChunks'], - methods: ['f.path=', 'f.path_components=', 'f.priority=', 'f.size_bytes=', 'f.size_chunks=', 'f.completed_chunks='], -}; - -module.exports = torrentFilePropsMap; diff --git a/shared/constants/torrentPeerPropsMap.js b/shared/constants/torrentPeerPropsMap.js deleted file mode 100644 index 68e23cc1c..000000000 --- a/shared/constants/torrentPeerPropsMap.js +++ /dev/null @@ -1,32 +0,0 @@ -const torrentPeerPropsMap = { - props: [ - 'address', - 'completedPercent', - 'clientVersion', - 'downloadRate', - 'downloadTotal', - 'uploadRate', - 'uploadTotal', - 'id', - 'peerRate', - 'peerTotal', - 'isEncrypted', - 'isIncoming', - ], - methods: [ - 'p.address=', - 'p.completed_percent=', - 'p.client_version=', - 'p.down_rate=', - 'p.down_total=', - 'p.up_rate=', - 'p.up_total=', - 'p.id=', - 'p.peer_rate=', - 'p.peer_total=', - 'p.is_encrypted=', - 'p.is_incoming=', - ], -}; - -module.exports = torrentPeerPropsMap; diff --git a/shared/constants/torrentStatusMap.js b/shared/constants/torrentStatusMap.js deleted file mode 100644 index 82ab21026..000000000 --- a/shared/constants/torrentStatusMap.js +++ /dev/null @@ -1,19 +0,0 @@ -const objectUtil = require('../util/objectUtil'); - -const torrentStatusMap = objectUtil.reflect({ - ch: 'checking', - sd: 'seeding', - p: 'paused', - c: 'complete', - d: 'downloading', - ad: 'activelyDownloading', - au: 'activelyUploading', - s: 'stopped', - e: 'error', - i: 'inactive', - a: 'active', -}); - -torrentStatusMap.statusShorthand = ['ch', 'sd', 'p', 'c', 'd', 'ad', 'au', 's', 'e', 'i', 'a']; - -module.exports = torrentStatusMap; diff --git a/shared/constants/torrentStatusMap.ts b/shared/constants/torrentStatusMap.ts new file mode 100644 index 000000000..df1e6b054 --- /dev/null +++ b/shared/constants/torrentStatusMap.ts @@ -0,0 +1,13 @@ +const torrentStatusMap = [ + 'checking', + 'seeding', + 'complete', + 'downloading', + 'stopped', + 'error', + 'inactive', + 'active', +] as const; + +export type TorrentStatus = typeof torrentStatusMap[number]; +export default torrentStatusMap; diff --git a/shared/constants/torrentTrackerPropsMap.js b/shared/constants/torrentTrackerPropsMap.js deleted file mode 100644 index f23759681..000000000 --- a/shared/constants/torrentTrackerPropsMap.js +++ /dev/null @@ -1,6 +0,0 @@ -const torrentTrackerPropsMap = { - props: ['group', 'url', 'id', 'minInterval', 'normalInterval', 'type'], - methods: ['t.group=', 't.url=', 't.id=', 't.min_interval=', 't.normal_interval=', 't.type='], -}; - -module.exports = torrentTrackerPropsMap; diff --git a/shared/schema/Auth.ts b/shared/schema/Auth.ts new file mode 100644 index 000000000..12b75ef67 --- /dev/null +++ b/shared/schema/Auth.ts @@ -0,0 +1,20 @@ +import {literal, nativeEnum, object, string, union} from 'zod'; +import type {infer as zodInfer} from 'zod'; + +import {AccessLevel} from './constants/Auth'; +import {clientConnectionSettingsSchema} from './ClientConnectionSettings'; + +export const authMethodSchema = union([literal('default'), literal('none')]); + +export type AuthMethod = zodInfer; + +export const credentialsSchema = object({ + username: string(), + password: string(), + client: clientConnectionSettingsSchema, + level: nativeEnum(AccessLevel), +}); + +export type Credentials = zodInfer; + +export type UserInDatabase = Required & {_id: string}; diff --git a/shared/schema/ClientConnectionSettings.ts b/shared/schema/ClientConnectionSettings.ts new file mode 100644 index 000000000..a08d4a673 --- /dev/null +++ b/shared/schema/ClientConnectionSettings.ts @@ -0,0 +1,68 @@ +import {literal, number, object, string, union} from 'zod'; +import type {infer as zodInfer} from 'zod'; + +const delugeConnectionSettingsSchema = object({ + client: literal('Deluge'), + type: literal('web'), + version: literal(1), + url: string().url(), + password: string(), +}); + +export type DelugeConnectionSettings = zodInfer; + +const qBittorrentConnectionSettingsSchema = object({ + client: literal('qBittorrent'), + type: literal('web'), + version: literal(1), + url: string().url(), + username: string(), + password: string(), +}); + +export type QBittorrentConnectionSettings = zodInfer; + +const rTorrentTCPConnectionSettingsSchema = object({ + client: literal('rTorrent'), + type: literal('tcp'), + version: literal(1), + host: string(), + port: number(), +}); + +export type RTorrentTCPConnectionSettings = zodInfer; + +const rTorrentSocketConnectionSettingsSchema = object({ + client: literal('rTorrent'), + type: literal('socket'), + version: literal(1), + socket: string(), +}); + +export type RTorrentSocketConnectionSettings = zodInfer; + +const rTorrentConnectionSettingsSchema = union([ + rTorrentTCPConnectionSettingsSchema, + rTorrentSocketConnectionSettingsSchema, +]); + +export type RTorrentConnectionSettings = zodInfer; + +const transmissionConnectionSettingsSchema = object({ + client: literal('Transmission'), + type: literal('rpc'), + version: literal(1), + url: string().url(), + username: string(), + password: string(), +}); + +export type TransmissionConnectionSettings = zodInfer; + +export const clientConnectionSettingsSchema = union([ + qBittorrentConnectionSettingsSchema, + rTorrentConnectionSettingsSchema, + transmissionConnectionSettingsSchema, +]); + +export type ClientConnectionSettings = zodInfer; diff --git a/shared/schema/Config.ts b/shared/schema/Config.ts new file mode 100644 index 000000000..54bec5403 --- /dev/null +++ b/shared/schema/Config.ts @@ -0,0 +1,159 @@ +// This is the config schema for Flood, a React-based frontend for various BitTorrent clients. +// This file provides the detailed documentation for config options. +// +// By default, is ~/.local/share/flood. +// +// You may use this schema to create a static configuration. However, it is not recommended. +// Stability of this schema can NOT be guaranteed. The reason is that other files import +// config.js and use its variables directly and it would involve unnecessary and duplicative +// conditionals in EVERY related file in order to retain compatibility for older config files. +// Plus, such duplications and conditionals are error-prone. +// +// Use CLI if you don't want to check and change the config.js whenever Flood is updated. +// CLI also supports passing through arguments via environment variables. For example, +// env variable FLOOD_OPTION_port=80 is equivalent to argument --port 80. Use ',' to split +// for arguments that take multiple inputs such as --allowedpath. + +import {array, boolean, number, object, string} from 'zod'; +import type {infer as zodInfer} from 'zod'; + +import {authMethodSchema} from './Auth'; +import {clientConnectionSettingsSchema} from './ClientConnectionSettings'; + +export const configSchema = object({ + // CLI argument: --baseuri + // This URI will prefix all of Flood's HTTP requests. + // For example, if you intend to serve from http://example.com/flood, set this to + // '/flood' and configure your web server to pass _all_ requests from `/flood` to + // the `/flood` of Flood's web server. [default: '/'] + baseURI: string(), + + // CLI argument: --dbclean + // Flood uses a local nedb database to keep track of users, torrents, and activity. The + // database is regularly purged to remove outdated data. This value dictates how old data + // is, in milliseconds, before being purged. [default: 1000 * 60 * 60] + dbCleanInterval: number().min(1000), + + // CLI argument: --rundir / -d + // Where to store the local nedb database. [default: '/db'] + dbPath: string(), + + // CLI argument: --rundir / -d + // Where to store Flood's temporary files [default: '/temp'] + tempPath: string(), + + // CLI argument: --auth (--noauth implies --auth=none) + // Authentication and user management method: [default: 'default'] + // + // default: + // Flood uses its own authentication and user management system. Users are authenticated + // by password and will be prompted to configure the connection to torrent client in the + // web interface. On successful authentication via /authenticate API endpoint, Flood will + // send a cookie with token to user. Users with admin privileges may create additional + // users with different password and torrent client configurations. + // + // none: + // There is no per-user config and no attempt to authenticate. An auth cookie with token is + // still needed to access API endpoints. This allows us to utilize browser's protections + // against session hijacking. The cookie with token will be sent unconditionally when + // /authenticate or /verify endpoints are accessed. Instead of per-user config, the + // configUser settings will be used. + authMethod: authMethodSchema, + + // CLI arguments: "When auth=none:" group + // Settings for the no-user configuration. + configUser: clientConnectionSettingsSchema.optional(), + + // CLI argument: --host / -h + // The host that Flood should listen for web connections on. + // To listen on all interfaces, change to `floodServerHost: '0.0.0.0'`. [default: '127.0.0.1'] + floodServerHost: string(), + + // CLI argument: --port / -p + // The port that Flood should listen for web connections on. [default: 3000] + floodServerPort: number().int().positive(), + + // CLI argument: --proxy + // Used for development only. Not used in production. + // See the "Local Development" section of README.md for detail. + floodServerProxy: string().url().optional(), + + // CLI argument: --maxhistorystates + // Flood keeps a history of torrent download and upload speeds. + // This value dictates the number of individual records per period to keep. + maxHistoryStates: number().int().positive(), + + // CLI argument: --clientpoll + // How often (in milliseconds) Flood will request the torrent list. This value affects how + // often values are updated when a user is present. {torrentClientPollIntervalIdle} will be + // used when no user is present. Note that poll intervals only affect activity stream. API + // requests like "GET /api/torrents" always trigger fresh torrent list fetch. [default: 1000 * 2] + torrentClientPollInterval: number().positive(), + + // CLI argument: --clientpollidle + // How often (in milliseconds) Flood will request the torrent list when no user is present. + // {torrentClientPollInterval} will be used when at least one user is present. This value + // usually affects some automations such as notification of download completion. Automations + // that rely on torrent properties may be delayed within the interval. [default: 1000 * 60 * 15] + torrentClientPollIntervalIdle: number().positive(), + + // CLI argument: --secret / -s + // A unique secret for signing messages with JWT (see https://jwt.io). + // Change this to something unique and hard to guess. + // You can use 'uuidgen' or 'cat /proc/sys/kernel/random/uuid' or 'uuidgenerator.net'. + // By default, this is a 72-character string randomly generated at the first launch. + // Generated secret is stored to "/flood.secret" with 0600 permissions. + secret: string().min(30), + + // CLI argument: --ssl + // Configuration for SSL, if using SSL with the Flood service directly. [default: false] + ssl: boolean().optional(), + + // CLI argument: --sslkey + // Path to the SSL private key. [default: '/key.pem'] + sslKey: string().optional(), + + // CLI argument: --sslcert + // Path to the SSL fullchain certificate. [default: '/fullchain.pem'] + sslCert: string().optional(), + + // Assign desired mounts to include. Refer to "Mounted on" column of `df -P` + // "undefined" means all possible mounts. [default: undefined] + watchMountPoints: array(string()).optional(), + + // CLI argument: --allowedpath, can be called multiple times + // Allowed paths for file operations. "undefined" means everything. [default: undefined] + allowedPaths: array(string()).optional(), + + // CLI argument: --assets + // Whether Flood should serve static assets. + // Users may prefer to serve static assets via a "professional" web server such as nginx to + // increase performance or have more flexibility on compression or other options. [default: true] + serveAssets: boolean().optional(), +}) + .refine( + (config) => { + if (config.authMethod === 'none') { + return config.configUser != null; + } + return true; + }, + { + message: 'Empty client connection settings.', + path: ['configUser'], + }, + ) + .refine( + (config) => { + if (config.ssl) { + return config.sslKey != null && config.sslCert != null; + } + return true; + }, + { + message: 'SSL key or certificate not specified.', + path: ['ssl'], + }, + ); + +export type Config = zodInfer; diff --git a/shared/schema/api/auth.ts b/shared/schema/api/auth.ts new file mode 100644 index 000000000..de84be4f2 --- /dev/null +++ b/shared/schema/api/auth.ts @@ -0,0 +1,50 @@ +import type {infer as zodInfer} from 'zod'; + +import {AccessLevel} from '../constants/Auth'; +import {credentialsSchema} from '../Auth'; + +import type {AuthMethod} from '../Auth'; + +// All auth requests are schema validated to ensure security. + +// POST /api/auth/authenticate +export const authAuthenticationSchema = credentialsSchema.pick({ + username: true, + password: true, +}); +export type AuthAuthenticationOptions = Required>; + +// POST /api/auth/authenticate - success response +export interface AuthAuthenticationResponse { + success: boolean; + username: string; + level: AccessLevel; +} + +// POST /api/auth/register +export const authRegistrationSchema = credentialsSchema; +export type AuthRegistrationOptions = Required>; + +// PATCH /api/auth/users/{username} +export const authUpdateUserSchema = credentialsSchema.partial(); +export type AuthUpdateUserOptions = zodInfer; + +// GET /api/auth/verify - preload configurations +export interface AuthVerificationPreloadConfigs { + authMethod: AuthMethod; + pollInterval: number; +} + +// GET /api/auth/verify - success response +export type AuthVerificationResponse = ( + | { + initialUser: true; + } + | { + initialUser: false; + username: string; + level: AccessLevel; + } +) & { + configs: AuthVerificationPreloadConfigs; +}; diff --git a/shared/schema/api/torrents.ts b/shared/schema/api/torrents.ts new file mode 100644 index 000000000..54cd6f801 --- /dev/null +++ b/shared/schema/api/torrents.ts @@ -0,0 +1,56 @@ +import {array, boolean, object, record, string} from 'zod'; +import {noComma} from '../../util/regEx'; + +import type {infer as zodInfer} from 'zod'; + +const TAG_NO_COMMA_MESSAGE = { + message: 'Tag must not contain comma', +}; + +// POST /api/torrents/add-urls +export const addTorrentByURLSchema = object({ + // URLs to download torrents from + urls: array(string()).nonempty(), + // Cookies to attach to requests, arrays of strings in the format "name=value" with domain as key + cookies: record(array(string())).optional(), + // Path of destination + destination: string().optional(), + // Tags + tags: array(string().regex(noComma, TAG_NO_COMMA_MESSAGE)).optional(), + // Whether destination is the base path [default: false] + isBasePath: boolean().optional(), + // Whether destination contains completed contents [default: false] + isCompleted: boolean().optional(), + // Whether to start torrent [default: false] + start: boolean().optional(), +}); + +export type AddTorrentByURLOptions = zodInfer; + +// POST /api/torrents/add-files +export const addTorrentByFileSchema = object({ + // Torrent files in base64 + files: array(string()).nonempty(), + // Path of destination + destination: string().optional(), + // Tags + tags: array(string().regex(noComma, TAG_NO_COMMA_MESSAGE)).optional(), + // Whether destination is the base path [default: false] + isBasePath: boolean().optional(), + // Whether destination contains completed contents [default: false] + isCompleted: boolean().optional(), + // Whether to start torrent [default: false] + start: boolean().optional(), +}); + +export type AddTorrentByFileOptions = zodInfer; + +// PATCH /api/torrents/tags +export const setTorrentsTagsSchema = object({ + // An array of string representing hashes of torrents to operate on + hashes: array(string()).nonempty(), + // An array of string representing tags + tags: array(string().regex(noComma, TAG_NO_COMMA_MESSAGE)), +}); + +export type SetTorrentsTagsOptions = zodInfer; diff --git a/shared/schema/constants/Auth.ts b/shared/schema/constants/Auth.ts new file mode 100644 index 000000000..8c4c984ef --- /dev/null +++ b/shared/schema/constants/Auth.ts @@ -0,0 +1,4 @@ +export enum AccessLevel { + USER = 5, + ADMINISTRATOR = 10, +} diff --git a/shared/schema/constants/ClientConnectionSettings.ts b/shared/schema/constants/ClientConnectionSettings.ts new file mode 100644 index 000000000..8d140018d --- /dev/null +++ b/shared/schema/constants/ClientConnectionSettings.ts @@ -0,0 +1 @@ +export const SUPPORTED_CLIENTS = ['qBittorrent', 'rTorrent', 'Transmission'] as const; diff --git a/shared/types/ClientSettings.ts b/shared/types/ClientSettings.ts new file mode 100644 index 000000000..49ee8aeed --- /dev/null +++ b/shared/types/ClientSettings.ts @@ -0,0 +1,29 @@ +export interface ClientSettings { + dht: boolean; + dhtPort: number; + directoryDefault: string; + networkHttpMaxOpen: number; + networkLocalAddress: Array; + networkMaxOpenFiles: number; + networkPortOpen: boolean; + networkPortRandom: boolean; + networkPortRange: string; + piecesHashOnCompletion: boolean; + piecesMemoryMax: number; + protocolPex: boolean; + // B/s + throttleGlobalDownSpeed: number; + // B/s + throttleGlobalUpSpeed: number; + throttleMaxPeersNormal: number; + throttleMaxPeersSeed: number; + throttleMaxDownloads: number; + throttleMaxDownloadsGlobal: number; + throttleMaxUploads: number; + throttleMaxUploadsGlobal: number; + throttleMinPeersNormal: number; + throttleMinPeersSeed: number; + trackersNumWant: number; +} + +export type ClientSetting = keyof ClientSettings; diff --git a/shared/types/DiskUsage.ts b/shared/types/DiskUsage.ts new file mode 100644 index 000000000..21d0f7a0b --- /dev/null +++ b/shared/types/DiskUsage.ts @@ -0,0 +1,8 @@ +export interface Disk { + target: string; + size: number; + avail: number; + used: number; +} + +export type Disks = Array; diff --git a/shared/types/Feed.ts b/shared/types/Feed.ts new file mode 100644 index 000000000..287d1931c --- /dev/null +++ b/shared/types/Feed.ts @@ -0,0 +1,54 @@ +export interface Feed { + type: 'feed'; + // Unique ID of the feed, generated by server by the time the feed is added. + _id: string; + // User-facing label (name) of the feed. + label: string; + // URL of the feed. + url: string; + // Interval between checking the feed for new items. (in minutes) + interval: number; + // How many times rules have matched items of the feed. + count?: number; +} + +export interface Rule { + type: 'rule'; + // Unique ID of the rule, generated by server by the time the automation is added. + _id: string; + // User-facing label (name) of the rule. + label: string; + // IDs of feeds of which this rule should apply to. + // TODO: Multi feed support not implemented. Only feedIDs[0] is used at the moment. + feedIDs: Array; + // Field of the feed item to be matched with rules. [default: 'title'] + field?: string; + // Regular expression to match items. + match: string; + // Regular expression to exclude items. + exclude: string; + // Destination path where matched items are downloaded to. + destination: string; + // Tags to be added when items are queued for download. + tags: Array; + // Start to download immediately after the items are queued. + startOnLoad: boolean; + // Whether the destination path should be used as base path. + isBasePath?: boolean; + // How many items this rule has matched. + count?: number; +} + +export interface MatchedTorrents { + type: 'matchedTorrents'; + _id: string; + // Previously queued URLs from matched feed items. So we don't queue them again. + urls: Array; +} + +export interface Item { + // Title of the feed item. + title: string; + // URLs extracted from the feed item. + urls: Array; +} diff --git a/shared/types/FloodSettings.ts b/shared/types/FloodSettings.ts new file mode 100644 index 000000000..985a8014b --- /dev/null +++ b/shared/types/FloodSettings.ts @@ -0,0 +1,45 @@ +import type {Language} from '../../client/src/javascript/constants/Languages'; +import type {TorrentContextMenuAction} from '../../client/src/javascript/constants/TorrentContextMenuActions'; +import type {TorrentListColumn} from '../../client/src/javascript/constants/TorrentListColumns'; + +export interface FloodSettings { + language: Language; + sortTorrents: { + direction: 'desc' | 'asc'; + property: TorrentListColumn; + }; + torrentListColumns: Array<{ + id: TorrentListColumn; + visible: boolean; + }>; + torrentListColumnWidths: Record; + torrentContextMenuActions: Array<{ + id: TorrentContextMenuAction; + visible: boolean; + }>; + torrentListViewSize: 'condensed' | 'expanded'; + speedLimits: { + download: Array; + upload: Array; + }; + mountPoints: Array; + + // Last selection state of "Delete data" toggle + deleteTorrentData: boolean; + + // Last selection state of "Start Torrent" toggle + startTorrentsOnLoad: boolean; + + // Preferred download destinations by tags + // currently set to the last used download destinations + // value of property '' is the default preferred destination + torrentDestinations?: Record; + + // Tag selector preference + UITagSelectorMode?: 'single' | 'multi'; + + // Last used "Add Torrents" tab + UITorrentsAddTab?: 'by-url' | 'by-file' | 'by-creation'; +} + +export type FloodSetting = keyof FloodSettings; diff --git a/shared/types/Notification.ts b/shared/types/Notification.ts new file mode 100644 index 000000000..e2c8867c7 --- /dev/null +++ b/shared/types/Notification.ts @@ -0,0 +1,41 @@ +export interface BaseNotification { + _id?: string; + read: boolean; + ts: number; // timestamp +} + +export interface TorrentNotification extends BaseNotification { + id: 'notification.torrent.finished' | 'notification.torrent.errored'; + data: { + name: string; + }; +} + +export interface FeedNotification extends BaseNotification { + id: 'notification.feed.torrent.added'; + data: { + title: string; + feedLabel: string; + ruleLabel: string; + }; +} + +export type Notification = TorrentNotification | FeedNotification; + +export interface NotificationCount { + total: number; + unread: number; + read: number; +} + +export interface NotificationState { + count: NotificationCount; + notifications: Array; +} + +export interface NotificationFetchOptions { + id: string; + limit: number; + start: number; + allNotifications?: boolean; +} diff --git a/shared/types/ServerEvents.ts b/shared/types/ServerEvents.ts new file mode 100644 index 000000000..d240f02d6 --- /dev/null +++ b/shared/types/ServerEvents.ts @@ -0,0 +1,23 @@ +import type {Operation} from 'fast-json-patch'; + +import type {Disks} from './DiskUsage'; +import type {NotificationCount} from './Notification'; +import type {Taxonomy} from './Taxonomy'; +import type {TorrentList} from './Torrent'; +import type {TransferHistory, TransferSummary} from './TransferData'; + +// type: data +export interface ServerEvents { + CLIENT_CONNECTIVITY_STATUS_CHANGE: { + isConnected: boolean; + }; + DISK_USAGE_CHANGE: Disks; + NOTIFICATION_COUNT_CHANGE: NotificationCount; + TAXONOMY_FULL_UPDATE: Taxonomy; + TAXONOMY_DIFF_CHANGE: Operation[]; + TORRENT_LIST_FULL_UPDATE: TorrentList; + TORRENT_LIST_DIFF_CHANGE: Operation[]; + TRANSFER_HISTORY_FULL_UPDATE: TransferHistory; + TRANSFER_SUMMARY_FULL_UPDATE: TransferSummary; + TRANSFER_SUMMARY_DIFF_CHANGE: Operation[]; +} diff --git a/shared/types/Taxonomy.ts b/shared/types/Taxonomy.ts new file mode 100644 index 000000000..8788ec3b4 --- /dev/null +++ b/shared/types/Taxonomy.ts @@ -0,0 +1,5 @@ +export interface Taxonomy { + statusCounts: Record; + tagCounts: Record; + trackerCounts: Record; +} diff --git a/shared/types/Torrent.ts b/shared/types/Torrent.ts new file mode 100644 index 000000000..13cad44e7 --- /dev/null +++ b/shared/types/Torrent.ts @@ -0,0 +1,54 @@ +import type {TorrentContent} from './TorrentContent'; +import type {TorrentPeer} from './TorrentPeer'; +import type {TorrentStatus} from '../constants/torrentStatusMap'; +import type {TorrentTracker} from './TorrentTracker'; + +export interface TorrentDetails { + contents: Array; + peers: Array; + trackers: Array; +} + +export enum TorrentPriority { + DO_NOT_DOWNLOAD = 0, + LOW = 1, + NORMAL = 2, + HIGH = 3, +} + +export interface TorrentProperties { + bytesDone: number; + dateAdded: number; + dateCreated: number; + directory: string; + downRate: number; + downTotal: number; + // Torrent ETA (seconds), -1 means infinity + eta: number; + hash: string; + isPrivate: boolean; + message: string; + name: string; + peersConnected: number; + peersTotal: number; + percentComplete: number; + priority: TorrentPriority; + ratio: number; + seedsConnected: number; + seedsTotal: number; + sizeBytes: number; + status: Array; + tags: Array; + trackerURIs: Array; + upRate: number; + upTotal: number; +} + +export interface TorrentList { + [hash: string]: TorrentProperties; +} + +export interface TorrentListSummary { + id: number; + torrents: TorrentList; +} diff --git a/shared/types/TorrentContent.ts b/shared/types/TorrentContent.ts new file mode 100644 index 000000000..76217f98f --- /dev/null +++ b/shared/types/TorrentContent.ts @@ -0,0 +1,31 @@ +export enum TorrentContentPriority { + DO_NOT_DOWNLOAD = 0, + NORMAL = 1, + HIGH = 2, +} + +export interface TorrentContent { + index: number; + path: string; + filename: string; + percentComplete: number; + priority: TorrentContentPriority; + sizeBytes: number; +} + +export interface TorrentContentSelection { + type: 'file' | 'directory'; + depth: number; + path: Array; + select: boolean; +} + +export interface TorrentContentSelectionTree { + isSelected?: boolean; + files?: { + [fileName: string]: TorrentContent & {isSelected?: boolean}; + }; + directories?: { + [directoryName: string]: TorrentContentSelectionTree; + }; +} diff --git a/shared/types/TorrentFile.ts b/shared/types/TorrentFile.ts new file mode 100644 index 000000000..d23f1204d --- /dev/null +++ b/shared/types/TorrentFile.ts @@ -0,0 +1,72 @@ +// Strings are Buffers from bencode data structure point of view. +// Timestamp is in second. + +export interface TorrentFile { + announce: Buffer; // main tracker + 'announce-list'?: Array>; // multi tracker torrent + comment?: Buffer; + 'created by': Buffer; + 'creation date': number; // timestamp + encoding?: Buffer; + info: { + length?: number; // single file torrent + files?: Array<{ + length: number; + path: Array; + }>; // multi file torrent + name: Buffer; + 'piece length': number; + pieces: Buffer; // hash tree, NOT string + private?: 0 | 1; + source?: Buffer; + }; +} + +export enum LibTorrentFilePriority { + OFF = 0, + NORMAL = 1, + HIGH = 2, +} + +export interface LibTorrentResume { + bitfield: number; + files: Array<{ + completed: number; // number of completed pieces + mtime: number; // timestamp + priority: LibTorrentFilePriority; + }>; + peers?: Array<{ + failed: 0 | 1; + inet: Buffer; // encoded IP address, NOT string + last: number; // timestamp + }>; + trackers?: { + [url: string]: { + enabled: 0 | 1; + }; + }; + 'uncertain_pieces.timestamp'?: number; // timestamp +} + +export interface RTorrentSession { + chunks_done: number; + chunks_wanted: number; + complete: 0 | 1; + directory: Buffer; + // 0: No hashing is happening. + // 1: The very first hash check is occurring. + // 2: The torrent is in the middle of hashing due to the finish event. + // 3: A rehash is occurring. + hashing: 0 | 1 | 2 | 3; + state: 0 | 1; + state_changed: number; // timestamp + state_counter: number; + tied_to_file: Buffer; + 'timestamp.finished': number; // timestamp + 'timestamp.started': number; // timestamp +} + +export interface RTorrentFile extends TorrentFile { + libtorrent_resume?: LibTorrentResume; + rtorrent?: RTorrentSession; +} diff --git a/shared/types/TorrentPeer.ts b/shared/types/TorrentPeer.ts new file mode 100644 index 000000000..1cf6d3e6f --- /dev/null +++ b/shared/types/TorrentPeer.ts @@ -0,0 +1,10 @@ +export interface TorrentPeer { + address: string; + country: string; + clientVersion: string; + completedPercent: number; + downloadRate: number; + uploadRate: number; + isEncrypted: boolean; + isIncoming: boolean; +} diff --git a/shared/types/TorrentTracker.ts b/shared/types/TorrentTracker.ts new file mode 100644 index 000000000..0c7685f07 --- /dev/null +++ b/shared/types/TorrentTracker.ts @@ -0,0 +1,10 @@ +export enum TorrentTrackerType { + HTTP = 1, + UDP = 2, + DHT = 3, +} + +export interface TorrentTracker { + url: string; + type: TorrentTrackerType; +} diff --git a/shared/types/TransferData.ts b/shared/types/TransferData.ts new file mode 100644 index 000000000..db2fd9b02 --- /dev/null +++ b/shared/types/TransferData.ts @@ -0,0 +1,21 @@ +export interface TransferSummary { + // Global download rate in B/s + downRate: number; + // Data downloaded this session in bytes + downTotal: number; + // Global upload rate in B/s + upRate: number; + // Data uploaded this session in bytes + upTotal: number; +} + +export type TransferDirection = 'upload' | 'download'; + +export type TransferData = Record; + +export interface TransferSnapshot extends TransferData { + numUpdates?: number; + timestamp: number; +} + +export type TransferHistory = Record>; diff --git a/shared/types/api/client.ts b/shared/types/api/client.ts new file mode 100644 index 000000000..f67b969a1 --- /dev/null +++ b/shared/types/api/client.ts @@ -0,0 +1,4 @@ +import type {ClientSettings} from '../ClientSettings'; + +// PATCH /api/client/settings +export type SetClientSettingsOptions = Partial; diff --git a/shared/types/api/feed-monitor.ts b/shared/types/api/feed-monitor.ts new file mode 100644 index 000000000..890b2c5a2 --- /dev/null +++ b/shared/types/api/feed-monitor.ts @@ -0,0 +1,7 @@ +import type {Feed, Rule} from '../Feed'; + +export type AddFeedOptions = Omit; + +export type ModifyFeedOptions = Partial; + +export type AddRuleOptions = Omit; diff --git a/shared/types/api/index.ts b/shared/types/api/index.ts new file mode 100644 index 000000000..bd467b037 --- /dev/null +++ b/shared/types/api/index.ts @@ -0,0 +1,4 @@ +import type {FloodSettings} from '../FloodSettings'; + +// PATCH /api/settings +export type SetFloodSettingsOptions = Partial; diff --git a/shared/types/api/torrents.ts b/shared/types/api/torrents.ts new file mode 100644 index 000000000..266a55e0e --- /dev/null +++ b/shared/types/api/torrents.ts @@ -0,0 +1,88 @@ +import type {TorrentPriority, TorrentProperties} from '../Torrent'; +import type {TorrentContentPriority} from '../TorrentContent'; + +// POST /api/torrents/create +export interface CreateTorrentOptions { + // Name of the torrent: + // For multi-file torrents, this becomes the base directory + // For single-file torrents, this becomes the filename + name?: string; + // Path to the file of folder + sourcePath: string; + // Trackers + trackers: Array; + // Optional comment in torrent file + comment?: string; + // Optional source entry in infohash + infoSource?: string; + // Whether the torrent is private + isPrivate: boolean; + // Whether to start torrent + start?: boolean; + // Tags, not added to torrent file + tags?: Array; +} + +// POST /api/torrents/check-hash +export interface CheckTorrentsOptions { + // An array of string representing hashes of torrents to be checked + hashes: Array; +} + +// POST /api/torrents/delete +export interface DeleteTorrentsOptions { + // An array of string representing hashes of torrents to be removed + hashes: Array; + // Whether to delete data of torrents + deleteData?: boolean; +} + +// POST /api/torrents/move +export interface MoveTorrentsOptions { + // Hashes of torrents to be moved + hashes: Array; + // Path of destination + destination: string; + // Whether to move data of torrents + moveFiles: boolean; + // Whether destination is the base path + isBasePath: boolean; + // Whether to check hash after completion + isCheckHash: boolean; +} + +// POST /api/torrents/start +export interface StartTorrentsOptions { + // An array of string representing hashes of torrents to be started + hashes: Array; +} + +// POST /api/torrents/stop +export interface StopTorrentsOptions { + // An array of string representing hashes of torrents to be stopped + hashes: Array; +} + +// PATCH /api/torrents/priority +export interface SetTorrentsPriorityOptions { + // An array of string representing hashes of torrents to operate on + hashes: Array; + // Number representing priority + priority: TorrentPriority; +} + +// PATCH /api/torrents/trackers +export interface SetTorrentsTrackersOptions { + // An array of string representing hashes of torrents to operate on + hashes: Array; + // URLs of trackers to be added to the torrents + trackers: Array; +} + +// PATCH /api/torrents/{hash}/contents +export interface SetTorrentContentsPropertiesOptions { + // An array of number representing indices of contents of a torrent + indices: Array; + // Number representing priority + priority: TorrentContentPriority; +} diff --git a/shared/util/formatUtil.js b/shared/util/formatUtil.js deleted file mode 100644 index fcaefdf47..000000000 --- a/shared/util/formatUtil.js +++ /dev/null @@ -1,71 +0,0 @@ -const moment = require('moment'); - -const formatUtil = { - secondsToDuration: cumSeconds => { - const years = Math.floor(cumSeconds / 31536000); - const weeks = Math.floor((cumSeconds % 31536000) / 604800); - const days = Math.floor(((cumSeconds % 31536000) % 604800) / 86400); - const hours = Math.floor((((cumSeconds % 31536000) % 604800) % 86400) / 3600); - const minutes = Math.floor(((((cumSeconds % 31536000) % 604800) % 86400) % 3600) / 60); - const seconds = Math.floor(cumSeconds - minutes * 60); - let timeRemaining = null; - - if (years > 0) { - timeRemaining = {years, weeks, cumSeconds}; - } else if (weeks > 0) { - timeRemaining = {weeks, days, cumSeconds}; - } else if (days > 0) { - timeRemaining = {days, hours, cumSeconds}; - } else if (hours > 0) { - timeRemaining = {hours, minutes, cumSeconds}; - } else if (minutes > 0) { - timeRemaining = {minutes, seconds, cumSeconds}; - } else { - timeRemaining = {seconds, cumSeconds}; - } - - return timeRemaining; - }, - - minToHumanReadable: min => moment.duration(min * 60 * 1000).humanize(), - - parsePeers: string => { - // This lovely delimiter is defined in clientResponseUtil. - const markerPosition = string.indexOf('@!@'); - return string.substr(0, markerPosition); - }, - - status: (isHashChecking, isComplete, isOpen, uploadRate, downloadRate, state, message) => { - const torrentStatus = []; - - if (isHashChecking === '1') { - torrentStatus.push('ch'); // checking - } else if (isComplete === '1' && isOpen === '1' && state === '1') { - torrentStatus.push('sd'); // seeding - } else if (isComplete === '1' && isOpen === '1' && state === '0') { - torrentStatus.push('p'); // paused - } else if (isComplete === '1' && isOpen === '0') { - torrentStatus.push('c'); // complete - } else if (isComplete === '0' && isOpen === '1' && state === '1') { - torrentStatus.push('d'); // downloading - } else if (isComplete === '0' && isOpen === '1' && state === '0') { - torrentStatus.push('p'); // paused - } else if (isComplete === '0' && isOpen === '0') { - torrentStatus.push('s'); // stopped - } - - if (message.length) { - torrentStatus.push('e'); // error - } - - if (uploadRate === '0' && downloadRate === '0') { - torrentStatus.push('i'); - } else { - torrentStatus.push('a'); - } - - return torrentStatus; - }, -}; - -module.exports = formatUtil; diff --git a/shared/util/objectUtil.js b/shared/util/objectUtil.js deleted file mode 100644 index dabe7308c..000000000 --- a/shared/util/objectUtil.js +++ /dev/null @@ -1,72 +0,0 @@ -const diffActionTypes = require('../constants/diffActionTypes'); - -const objectUtil = { - createStringMapFromArray: (array = []) => - array.reduce((memo, key) => { - memo[key] = key; - - return memo; - }, {}), - - createSymbolMapFromArray: (array = []) => - array.reduce((memo, key) => { - memo[key] = Symbol(key); - - return memo; - }, {}), - - getDiff: (prevObject = {}, nextObject = {}) => { - const prevObjectKeys = Object.keys(prevObject); - const nextObjectKeys = Object.keys(nextObject); - - let shouldCheckForRemovals = nextObjectKeys.length < prevObjectKeys.length; - - const diff = nextObjectKeys.reduce((accumulator, key) => { - const prevValue = prevObject[key]; - const nextValue = nextObject[key]; - - if (prevValue == null) { - shouldCheckForRemovals = true; - - accumulator.push({ - action: diffActionTypes.ITEM_ADDED, - data: { - [key]: nextValue, - }, - }); - } else if (prevValue !== nextValue) { - accumulator.push({ - action: diffActionTypes.ITEM_CHANGED, - data: { - [key]: nextValue, - }, - }); - } - - return accumulator; - }, []); - - if (shouldCheckForRemovals) { - prevObjectKeys.forEach(key => { - if (nextObject[key] == null) { - diff.push({ - action: diffActionTypes.ITEM_REMOVED, - data: key, - }); - } - }); - } - - return diff; - }, - - reflect: object => - Object.keys(object).reduce((memo, key) => { - memo[key] = object[key]; - memo[object[key]] = key; - - return memo; - }, {}), -}; - -module.exports = objectUtil; diff --git a/shared/util/regEx.js b/shared/util/regEx.js deleted file mode 100644 index 3d608307a..000000000 --- a/shared/util/regEx.js +++ /dev/null @@ -1,7 +0,0 @@ -const regEx = { - url: /^(?:https?|ftp):\/\/.{1,}\.{1}.{1,}/, - domainName: /(?:https?|udp):\/\/(?:www\.)?([-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,18}\b)*(\/[/\d\w.-]*)*(?:[?])*(.+)*/i, - cdata: //, -}; - -module.exports = regEx; diff --git a/shared/util/regEx.ts b/shared/util/regEx.ts new file mode 100644 index 000000000..df78eb366 --- /dev/null +++ b/shared/util/regEx.ts @@ -0,0 +1,4 @@ +export const url = /^(?:https?|ftp):\/\/.{1,}\.{1}.{1,}/; +export const domainName = /(?:https?|udp):\/\/(?:www\.)?([-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,18}\b)*(\/[/\d\w.-]*)*(?:[?])*(.+)*/i; +export const cdata = //; +export const noComma = /^[^,]+$/; diff --git a/shared/util/stringUtil.js b/shared/util/stringUtil.js deleted file mode 100644 index 4af16faf8..000000000 --- a/shared/util/stringUtil.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - capitalize: string => string.charAt(0).toUpperCase() + string.slice(1), - - pluralize: (string, count) => { - if (count !== 1) { - if (string.charAt(string.length - 1) === 'y') { - return `${string.substring(0, string.length - 1)}ies`; - } - return `${string}s`; - } - - return string; - }, - - withoutTrailingSlash: input => input.replace(/\/{1,}$/, ''), -}; diff --git a/shared/util/stringUtil.ts b/shared/util/stringUtil.ts new file mode 100644 index 000000000..c1b842895 --- /dev/null +++ b/shared/util/stringUtil.ts @@ -0,0 +1,3 @@ +export default { + withoutTrailingSlash: (input: string): string => input.replace(/\/{1,}$/, ''), +}; diff --git a/tsconfig.json b/tsconfig.json index 3d0d84121..d12af2c28 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,15 +5,20 @@ "target": "esnext", "moduleResolution": "node", "allowJs": true, + "module": "esnext", "noEmit": true, "strict": true, "isolatedModules": true, + "resolveJsonModule": true, + "skipLibCheck": true, "esModuleInterop": true, + "experimentalDecorators": true, + "useDefineForClassFields": true, "baseUrl": "./", "paths": { "@shared/*": ["shared/*"] } }, - "include": ["./client/**/*.ts", "./client/**/*.tsx", "./server/**/*.ts", "./server/**/*.tsx", "./custom.d.ts"], - "exclude": ["node_modules", "**/*.spec.ts"] + "include": ["./client/**/*.ts", "./client/**/*.tsx", "./server/**/*.ts", "./server/**/*.tsx"], + "exclude": ["node_modules"] } diff --git a/vercel.json b/vercel.json new file mode 100644 index 000000000..80e57248a --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "github": { + "enabled": false + } +}